[comp.lang.perl] implicit limit on number of members in netgroup

aks@hub.ucsb.edu, , ks (Alan Stebbens) (12/05/90)

| is there some arbitrary ceiling on the number of machines one can
| define for membership in a netgroup id in the /etc/netgroup file ??  I
| have evidence that there is some limit.  Would like to know if others
| have similar suspicions.

Yes.  If you are using the older YP routines, they use "dbm" maps, which
have an internal limit of 1024 characters per record.  Even the newer
versions, using "ndbm", still have the internal limit, it's just a
little bigger, at 4096 bytes per record.

To get around this limitation for potentially large netgroups (like
"undergrads" in our Computer Science YP -- err, I mean -- NIS domain),
we wrote a little Perl script to automatically convert our *real*
source, "/etc/netgroup.master", into the file required by "makedbm",
"/etc/netgroup", with no record larger than the configured size.  As
part of the conversion, large netgroups are detected and broken into
smaller sub-netgroups which are then chained together as part of the
original netgroup.  Also, the input syntax for "mknetgrp" is more
lenient than "makedbm" allows, offering a convenience to the humans
maintaining the netgroup file.

Here's a sample output:

    Script started on Tue Dec  4 14:39:47 1990
    # mknetgrp -v
    Input netgroup file is /etc/netgroup.master
    Output netgroup file is /etc/netgroup
    Maximum record length is 512 bytes
    cs-domain:          4 members,   70 bytes.
    cs-clients:         9 members,  143 bytes.
    cs-servers:         9 members,  143 bytes.
    cs-standalone:      5 members,   80 bytes.
    ccse-clients:       2 members,   58 bytes.
    csil-servers:       4 members,   73 bytes.
    csil-clients:       2 members,   55 bytes.
    cs-nextusers:       3 members,   60 bytes.
    ecl-clients:        5 members,   92 bytes.
    ecl-servers:        2 members,   50 bytes.
    ecl-users:          6 members,   88 bytes.
    tcl-clients:        2 members,   43 bytes.
    tcl-servers:        1 members,   34 bytes.
    tcl-users:          7 members,   99 bytes.
    rsl-clients:        7 members,  103 bytes.
    rsl-servers:        2 members,   50 bytes.
    rsl-users:         13 members,  164 bytes.
    gsl-clients:        7 members,  107 bytes.
    gsl-servers:        1 members,   36 bytes.
    gsl-users:          0 members,   21 bytes.
    csgrad-printer:     2 members,   53 bytes.
    cs-fac-clients:     2 members,   58 bytes.
    cs-fac-servers:     4 members,   75 bytes.
    cs-staff:           8 members,  108 bytes.
    cs-faculty:        28 members,  325 bytes.
    cs-research:        1 members,   38 bytes.
    cs-tempfaculty:    21 members,  263 bytes.
    cs-grad:            2 members,   39 bytes.
    cs-graduate:      375 members,  8 subgroups, 4471 bytes.
    cs-undergrad:     652 members, 14 subgroups, 7748 bytes.
    ccse-staff:        24 members,  293 bytes.
    ccse-consultant:    1 members,   43 bytes.
    ccse-help:         16 members,  227 bytes.
    cs-guest:           7 members,  104 bytes.
    ece-faculty:        1 members,   36 bytes.
    69 groups in; 91 groups out.
    # ^D
    script done on Tue Dec  4 14:40:16 1990


To fully automate this, you should modify the /var/yp/Makefile so that
the "netgroup.time" target causes an invocation of "mknetgrp" before
running "makedbm".

We've been using this script for almost two years with no problems.

The following Perl scripts and "man" pages are provided for your
benefit, gratis, caveat emptor. 

Alan Stebbens   Computer Resource Manager
		Center for Computational Sciences and Engineering (CCSE)
		University of California, Santa Barbara
		3111 Engineering I
		Santa Barbara, CA 93106

Internet: aks@hub.ucsb.edu
BITNET:   aks%hub@ucsbuxa.bitnet
UUCP:     ...{ucbvax,sdcsvax,cepu}!ucsbcsl!aks
Voice:    (805) 893-8135 (CCSE Office: 893-3221)

============================= cut here ===================================
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 1 (of 1)."
# Contents:  Makefile addnetgrp addnetgrp.8 mknetgrp mknetgrp.8
# Wrapped by aks@anywhere on Tue Dec  4 14:19:23 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f Makefile -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"Makefile\"
else
echo shar: Extracting \"Makefile\" \(758 characters\)
sed "s/^X//" >Makefile <<'END_OF_Makefile'
X# $Header: Makefile,v 1.4 89/01/20 13:22:43 aks Locked $
X#
XETC_DIR	=	$(DEST_DIR)/usr/local/etc
XPGMS	=	${ETC_DIR}/addnetgrp \
X		${ETC_DIR}/mknetgrp
XMAN	=	${MAN_DIR}/addnetgrp.8
X# Set HOSTS to a list of hosts to which these programs will be "rdist"ed.
XHOSTS	=	cornu raster
X
Xinstall:	${PGMS}
Xrdist:		install
X	rdist -d HOSTS='(${HOSTS})' -d PGMS='(${PGMS})'
X
XINSTALL_PGM	= install -c -m 550 -o root -g wheel $? $@
XINSTALL_MAN	= install -c -m 444 -o root -g bin $? $@
XMAKE_DIFF	= rcsdiff $? > $@
X
X$(ETC_DIR)/addnetgrp:	addnetgrp	; $(INSTALL_PGM)
X$(ETC_DIR)/mknetgrp:	mknetgrp	; $(INSTALL_PGM)
X$(MAN_DIR)/addnetgrp.8:	addnetgrp.8	; $(INSTALL_MAN)
X
Xdiff:		addnetgrp.diff mknetgrp.diff
Xaddnetgrp.diff:	addnetgrp	;	$(MAKE_DIFF)
Xmknetgrp.diff:	mknetgrp	;	$(MAKE_DIFF)
END_OF_Makefile
if test 758 -ne `wc -c <Makefile`; then
    echo shar: \"Makefile\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f addnetgrp -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"addnetgrp\"
else
echo shar: Extracting \"addnetgrp\" \(7037 characters\)
sed "s/^X//" >addnetgrp <<'END_OF_addnetgrp'
X#!/bin/perl
X# $Header: addnetgrp,v 1.7 88/10/11 11:03:01 root Exp $
X# This script creates new netgroups, adds netgroups and logins to existing 
X# netgroups, or removes members from netgroups, or removes entire netgroups.
X#
X# addnetgrp [-rvfunh] [-F netgroupfile] netgroup [member...]
X# rmnetgrp [-vfunh] [-F netgroupfile] netgroup [member...]
X#
X# where each "member" is either another netgroup name, to be added as a 
X# member, or a "tuple" of "machine,user,domain" optionally parenthesized.
X# Note, though, that as whitespace is used to seperate member tokens,
X# no space can occur around the commas, unless the tuple is parenthesized.
X#
X# If the -u option is given, the members are user logins, unless they are 
X# parenthetical tuples.  If the -h option is given, the member tokens are 
X# host names, otherwise they are tuples.  Without either the -u or -h 
X# options, the tokens are netgroup names.
X#
X# If the netgroup is given as "-", then the operation will be applied to 
X# all netgroups.
X#
X$me = $0;			# my name
X$rFlag = ($me =~ /rmnetgrp/);	# off for addnetgrp, on for rmgrp
X$vFlag = 0;			# verbose off by default
X$fFlag = 0;			# force off by default
X$uFlag = 0;			# recognize bare names as users
X$hFlag = 0;			# recognize bare names as hosts
X$nFlag = 0;			# recognize bare names as netgroups
X$netGroupFile = "/etc/netgroup.master";	# by default
X#
Xwhile ($_ = $ARGV[0], /^-/) {
X    shift;
X    goto Usage if /^-/ && /[^-uhrnvfF]/;
X    /^-.*r/ && $rFlag++;
X    /^-.*v/ && $vFlag++;
X    /^-.*f/ && $fFlag++;
X    /^-.*h/ && $hFlag++;
X    /^-.*n/ && $nFlag++;
X    /^-.*u/ && $uFlag++;
X    if (/^-F/) {
X	die "Missing filename after '-F' option\n" unless $#ARGV >= $[;
X	$netGroupFile = $ARGV[0];
X	shift;
X    }
X}
Xdie "Use only ONE of -h, -n, or -u!\n" if $nflag + $hFlag + $uFlag > 1;
Xgoto Usage if $#ARGV < $[;	# any more things left?
Xdie "'$netGroupFile' does not exist.\n" unless -f $netGroupFile;
X
X$_ = $ARGV[0]; shift;		# get the netgroup
Xif (/([^a-zA-Z0-9_-])/) {	# any illegal chars?
X    print "Illegal character '$1' in netgroup name: \"$_\"\n";
X    goto Usage;
X}
X$uFlag++ if !($nFlag || $hFlag || $uFlag);	# set default
X$netGroup = $_;
X@Members = ();			# empty the array
X$MemberStr = " ";		# string to recognize things with
Xforeach (@ARGV) {		# scan remaining args as members
X    $mem = '';
X    if (/^[A-Za-z0-9_-]+$/) {	# simple name?
X	$mem = "(,$_,)"	if $uFlag;	# recognize users
X	$mem = "($_,,)"	if $hFlag;	# recognize hosts
X	$mem = "$_"	if $nFlag;	# recognize netgroups
X    }
X    if (/^([a-z0-9_-]*),([a-z0-9-]*),([A-Za-z0-9_-]*)/ ||
X	/^\(?\s*([a-z0-9_-]*)\s*,\s*([a-z0-9-]*)\s*,\s*([A-Za-z0-9_-]*)\s*\)?/) {
X	next if /^,,?$/;	# ignore empty members
X	$mem = "($1,$2,$3)";	# get the member token
X    }
X    die "Illegal or badly formatted member: '$_'\n" unless $mem;
X    $qmem = $mem;
X    $qmem =~ s/([()])/\\$1/g;	# quote the parens
X    if ($MemberStr !~ /\s$qmem\s/) {	# in the list yet?
X	$MemberStr .= "$mem ";	# no, but it is now
X	push(@Members,$mem);	# add a new member
X    }
X}
X#
X# Now open the netgroup file, scan to the netgroup in question, and do the requested
X# operation.
X#
X$newNetGroupFile = "$netGroupFile.new";
Xopen(NGFIN,$netGroupFile) 
X    || die "Can't open '$netGroupFile' for input because $!\n";
Xif (-f $newNetGroupFile) {
X    $wait = 0;
X    print "(waiting...";
X    while (-f $newNetGroupFile && ++$wait < 60) { sleep 15; print ".";}
X    if (-f $newNetGroupFile) {
X	print "\nThe wait has been too long for $newNetGroupFile.\n";
X	print "Please examine the netgroup files and fix, if possible.\n";
X	exit 1;
X    }
X}
Xopen(NGFOUT,">$newNetGroupFile")
X    || die "Can't open '$newNetGroupFile' for output because $!\n";
X
X@GroupList = ();
X
X # copy up the netgroup file up to our group, if it exists
X
X$ng = $netGroup;
X$ng =~ s/^-$/\w+/;		# make it a regexp pattern
X$found = 0;
X
Xwhile (<NGFIN>) {
X    if (!s/^$ng\b//) { print NGFOUT; next; }
X    $found++;
X    $grpName = $&;		# get the real name
X    # yes, scan the remaining member lines, skipping comments, and place
X    # all the members in the group list
X    $tell = tell(NGFIN);
X    while ($_ && /^[\s#]/) {	# scan member lines
X	push(@GroupList,split(' ')) unless /^\s*#/;
X	$tell = tell(NGFIN);	# remember this position in case we backup
X	$_ = <NGFIN>;
X    }
X    # now, if we are removing the entire group, we're done!
X    if ($rFlag && $#Members < $[) {
X	print "Removing entire netgroup: $grpName...\n" if $vFlag;
X	goto Seek;		# skip rest of this
X    } elsif ($rFlag) {		# removing...
X	local(@removed,$groupstr);
X	$groupstr = ' ' . join(' ',@GroupList) . ' '; # get big string of all members
X	@removed = ();
X	foreach $mem (@Members) {		# scan the member removal list
X	    local($qmem) = $mem;
X	    $qmem =~ s/([()])/\\$1/g;		# quote the parens
X	    if ($groupstr =~ s/\s$qmem\s/ /g) {	# removed it?
X		push(@removed,$mem);		# yes, remember it
X	    } else {
X		print "$mem is not a member.\n" if $vFlag;
X	    }
X	}
X	if ($#removed >= $[) {			# anything removed?
X	    print "Removed ",join(' ',@removed)," from $grpName.\n" if $vFlag;
X	    @GroupList = split(' ',$groupstr);	# restore the group list
X	}
X    } else {		# adding...
X	local(@added,$groupstr);
X	$groupstr = ' ' . join(' ',@GroupList) . ' ';
X	@added = ();
X	foreach $mem (@Members) {
X	    local($qmem) = $mem;
X	    $qmem =~ s/([()])/\\$1/g;		# quote the parens
X	    if ($groupstr !~ /\s$qmem\s/) {
X		$groupstr .= " $mem";		# so future compares track new members
X		push(@added,$mem);			# remember new members
X	    } else {
X		print "$mem is already a member.\n" if $vFlag;
X	    }
X	}
X	if ($#added >= $[) {			# anything added?
X	    push(@GroupList,@added);		# add new groups
X	    print "Added ",join(' ',@added)," to $grpName.\n" if $vFlag;
X	}
X    }
X    # we need to restore what's left of the group (or the new, bigger group) to
X    # the file
X    print NGFOUT $grpName,"\n";
X    foreach (@GroupList) { print NGFOUT "\t$_\n"; }
X  Seek:
X    # prepare to continue the search
X    seek(NGFIN,$tell,0);
X}
Xclose NGFIN;
Xif (!$found) {				# any netgroups found?
X    if ($rFlag) {			# unless this is set
X	print "$netGroup does not exist.\n" unless $fFlag && !$vFlag;
X	close NGFOUT;
X	unlink "$newNetGroupFile";
X	exit 1;
X    }
X    print NGFOUT "#\n";
X    print NGFOUT $netGroup,"\n";	# add new netgroup name
X    print "$netGroup created.\n" if $vFlag;
X    foreach (@Members) {		# for any members of the new group
X	print NGFOUT "\t$_\n";		# output the member
X    }
X    print "Added ",join(' ',@Members)," to $netGroup\n" 
X	if $vFlag && $#Members >= $[;
X}
Xclose NGFOUT;
X$backup = "$netGroupFile.orig";
Xif (-f $backup) {			# orig already exists?
X    $backup = "$netGroupFile.old";
X    unlink $backup if -f $backup;
X}
Xrename($netGroupFile,$backup);
Xrename($newNetGroupFile,$netGroupFile);
Xexit 0;
X
X#
X# Tell user how to use the program.
X#
XUsage:
Xprint "Usage:$me [-vrnuhf] [netgroup|-] [names...|tuples...]\n";
Xprint "Options: -v = verbose, -r = remove (add otherwise),\n";
Xprint "-u = treat names as user logins, -h = treat names as hosts\n";
Xprint "-n = treat names as netgroups, -f = force (don't complain).\n";
Xexit 1;
END_OF_addnetgrp
if test 7037 -ne `wc -c <addnetgrp`; then
    echo shar: \"addnetgrp\" unpacked with wrong size!
fi
chmod +x addnetgrp
# end of overwriting check
fi
if test -f addnetgrp.8 -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"addnetgrp.8\"
else
echo shar: Extracting \"addnetgrp.8\" \(4571 characters\)
sed "s/^X//" >addnetgrp.8 <<'END_OF_addnetgrp.8'
X.TH ADDNETGRP 8 "October 10, 1988"
X.UC
X.SH NAME
Xaddnetgrp \- add netgroups and/or members to netgroups in the netgroup file.
X.LP
Xrmnetgrp  \- remove members from existing netgroup, or remove a netgroup
Xentirely.
X.SH SYNOPSIS
X.B addnetgrp
X[
X.B \-vrnuhf
X]  [
X.B \-F 
Xnetgroupfile ]  netgroup [ member ... ]
X.br
X.B rmnetgrp
X[
X.B \-vfuhn 
X]  [
X.B \-F 
Xnetgroupfile ]  netgroup [ member ... ]
X.SH DESCRIPTION
X.I addnetgrp
Xcan be used to create new netgroups,  or add new members
Xto existing or newly created netgroups, or remove members from an
Xexisting netgroups, or remove a netgroup entirely.
X.LP
X.I rmnetgrp
Xremoves members from an existing netgroup, or removes
Xa netgroup entirely.
X.LP
XThe netgroupfile affected is the /etc/netgroup.master by default.
XHowever, by using the -F option the netgroupfile affected can be changed.
XBoth
X.I adddnetgrp
Xand
X.I rmnetgrp
Xcan also work on either a single netgroup or all netgroups.  
XIf the netgroup is given as "-", all of the netgroups are affected. 
XThis is helpful when removing a certain member from all netgroups.
XHowever, they cannot work on a specific list of netgroups. 
X.LP
XAn important concept to the netgroup file is the "tuple".  This basic
Xunit helps define the format for members that belong to a particular
Xnetgroup.  A tuple may also be referred to as a "triple".  Members
Xof each netgroup can be a hostname, username, or domain-name, and
Xsometimes combinations of these are grouped together.
XThe members may also be a netgroup name that already has its own members;
Xand in this case, no tuple is required.
X.LP
XThe tuple has the following format:   
X.B (hostname,username,domainname)
X.LP
XFor the
X.I addnetgrp
Xand
X.I rmnetgrp
Xprograms,
Xthe -u and -h flags are important for the type of tuple you wish
Xto create.  With member(s) given in the command line, the -u option
Xis used to form a tuple(s) like (,member1,) (,member2,) ...  In 
Xother words, each member is treated as a username.  When
Xthe -h option is given, each member is treated as a hostname; and
Xthe tuple format looks like (member1,,) (member2,,) ... 
X.LP
XWhen
Xthe -n option flag is used, members are treated as netgroups themselves
Xand the member is taken literally.  So, no tuple is formed. Each
Xmember is added to the netgroup specified as shown in ascii
Xwith no parentheses and commas.
X.LP
X.B Note:
Xthe programs only work with one of these options (-u, -h, -n)
Xat a given time.  Also, if none of these options is given, the
Xmember is treated as a username by default with tuple form (,member,).
XRead the man pages for "netgroup" if you need more information
Xabout what tuples look like and how the netgroup file is set-up.
X.SH OPTIONS
X.TP
X.B \-v
XInitiate a verbose mode of what
X.I addnetgrp
Xor
X.I rmnetgrp
Xis doing.
X.TP
X.B \-r
XIf members are given, remove them from the netgroup given.
XIf no members are given, remove the entire netgroup.  This
Xis only used with
X.I addnetgrp.
X.TP
X.B \-n
XTreat members as netgroup names with no tuples formed.
X.TP
X.B \-f
XWhen adding, force addition without complaining about members'
Xnames that are already in the netgroup given.  When removing,
Xremove members without complaining that they are not in the
Xnetgroup specified.
X.TP
X.B \-u
XTreat members as user login names when adding or removing,
Xby using the tuple format (,member,).
X.TP
X.B \-h
XTreat members as host machine names, by using the tuple
Xformat (member,,).
X.TP
X.B \-F netgroupfile
XInstead of using the file /etc/netgroup.master as the default
Xnetgroupfile, use the netgroupfile specified in the command line.
XThe entire pathname must be given if it is in a different
Xdirectory.
X.SH EXAMPLES
X.br
X.IP
Xaddnetgrp cs-staff tom dick harry
X.LP
XThis first example adds the tuples (,tom,) (,dick,) (,harry,)
Xto the netgroup "cs-staff" in the default netgroup file
X(/etc/netgroup.master).
X.br
X.IP
Xaddnetgrp -rvh -F /etc/netgroup.orig csil-clients goofy mickey
X.LP
XThis example removes the tuples (goofy,,) (mickey,,)
Xfrom the netgroup "csil-clients" in the specified netgroup file 
X(/etc/netgroup.orig). It also displays messeges about the
Xtuples that were added to the netgroup.
X.br
X.IP
Xaddnetgrp -vn cs-clients new-clients old-clients
X.LP
XThis example does not add tuples, but adds the netgroups
Xnew-clients and old-clients themselves to the netgroup called
X"cs-clients."
X.br
X.IP
Xrmnetgrp -uf cs-faculty minnie daffy
X.LP
XThis last example removes the tuples (,minnie,) (,daffy,)
Xfrom the netgroup cs-faculty without complaining about 
Xusernames not in that particular netgroup.
X.SH FILES
X/etc/netgroup.master as the default netgroupfile.
X.SH BUGS
XNone reported.
END_OF_addnetgrp.8
if test 4571 -ne `wc -c <addnetgrp.8`; then
    echo shar: \"addnetgrp.8\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f mknetgrp -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"mknetgrp\"
else
echo shar: Extracting \"mknetgrp\" \(6201 characters\)
sed "s/^X//" >mknetgrp <<'END_OF_mknetgrp'
X#!/bin/perl
X# $Header: mknetgrp,v 1.6 89/08/04 12:00:49 aks Exp $
X# mknetgrp [-v] [-i input_file] [-o output_file] [-l record-length]
X#
X# This script will parse the netgroup input file, writing a source
X# suitable for parsing by "makedbm" (part of YP).
X#
X# The whole reason for this script is due to a gross bug in
X# dbm(3) (even ndbm(3) has the same limitation), in that no single
X# record can be longer than 1024 (or 4096 for ndbm) chars.
X# Netgroups of students can get QUITE large, and so must be broken
X# up into smaller groups.
X#
X# To make it easy, the human-source doesn't have to worry about
X# these restrictions, but the machine-source cannot have a
X# netgroup record exceeding the limitations of dbm.  This program
X# will parse each netgroup record, and, if it finds a large
X# record, breaks into smaller chucks with sequentially-named
X# subgroups.
X#
X# The algorithm makes two passes over the file: the first just
X# reads in all existing netgroup names; the second reads each
X# record and if it's not too large, writes it out immediately.  If
X# it is too large, then as much of the group members as will fit
X# onto the first record is written, including the new subgroup
X# name which is created from the symbolically-incremented parent
X# group name (an easy trick courtesy of Perl).  This is the reason
X# for pass 1: to ensure that any new subgroups names are unique.
X# The next subgroup is written, and then the process repeated
X# until the record is entirely written.
X#
X# The input format is more relaxed than the actual "makedbm" input
X# format: if the subsequent line begins with a space or tab, it is
X# assumed to be a continuation of the previous group, intervening
X# comment lines not withstanding (in other words, you can pretty
X# much place a comment line anywhere, without worrying about
X# interrupting a continuation).
X#
X# The above "freedom" implies that a netgroup record must begin
X# with the netgroup name in column one.  The items which follow
X# must be the subgroup names or triples.
X#
X# Some configuration parameters
X#
X$Debug = 0;
X$MaxRecLen = 512;		# max "ndbm" record size
X$MAXLINELEN = 80;		# for pretty output to terminal
X
X$[ = 0;				# origin 0 indexing
X@NetGroupNames = ();
X$NetGrpIn = "/etc/netgroup.master";
X$NetGrpOut = "/etc/netgroup";
X
X# Keep track of # of groups in /out.
X
X$InCount = 0;
X$OutCount = 0;
X$Verbose = 0;
X$MaxNameLen = 0;
X
X# Process any arguments
X
Xwhile ($_ = shift) {
X   if (!/^-[viodl]$/) {
X      print "usage: mknetgrp [-v] [-d] [-i in] [-o out] [-l maxrec]\n";
X      exit 1;
X   }
X   if (/^-i$/) { $NetGrpIn = shift; next; }
X   if (/^-o$/) { $NetGrpOut = shift; next; }
X   if (/^-l$/) { $MaxRecLen = int(shift); next; }
X   if (/^-v$/) { $Verbose++; next; }
X   if (/^-d$/) { $Debug++; next; }
X}
X
Xif ($Debug) {
X   $NetGrpIn = "netgroup.master";
X   $NetGrpOut = "netgroup";
X}
X
Xopen(NETGRPIN,$NetGrpIn) || die "Can't open $NetGrpIn\n";
Xif ($Verbose) {
X   print "Input netgroup file is $NetGrpIn\n";
X   print "Output netgroup file is $NetGrpOut\n";
X   printf "Maximum record length is %d bytes\n",$MaxRecLen;
X}
X
X# Pass 1: scan the file for just netgroup names
X
Xwhile (<NETGRPIN>) {
X   chop; s/\\$//;
X   next if /^\s/ || /^$/;
X   @line = split(' ');
X   $NetGroupNames{$line[0]} = 1;
X   if ($Verbose) {
X      local($len) = length($line[0]);
X      $MaxNameLen = $len if $len > $MaxNameLen;
X   }
X}
X
X# Open the output file
X
Xseek(NETGRPIN,0,0) || "Could not rewind $NetGrpIn!\n";
X
Xopen(NETGRPOUT,">$NetGrpOut.tmp")
X	|| die "Can't open $NetGrpOut for output!\n";
X
Xprint NETGRPOUT 
X      "# This netgroup file was produced automatically by \"mknetgrp\"\n";
X@lt = localtime(time);
Xprintf NETGRPOUT "# on %02d/%02d/%02d from %s\n",$lt[4],$lt[3],$lt[5],$NetGrpIn;
Xprint NETGRPOUT "# DO NOT MODIFY THIS FILE!\n";
X
X@Group = ();
X
Xwhile (<NETGRPIN>) {
X   chop;
X   next if /^$/ || /^#/ || /^\s*#/; # don't write blanks or comments
X   $cont = /^[\t ]/;		# set flag if continuation
X   s/\s(#.*|\\)$//;		# strip any comments & continuations
X   s/\s*,\s*/,/g;		# remove blanks inside of tuples
X   s/\(\s+/(/g;
X   s/\s+\)/)/g;
X   s/\\$//;
X   @list = split(' ');
X   if (!$cont) {		# if new list
X      $OutCount += do PrintGroup() if $#Group >= $[;
X      $InCount++;
X      @Group = @list;
X      if ($Verbose) {
X	 local($name) = $Group[0];
X	 local($len) = length($name);
X	 printf "%s:%s",$name,(" " x ($MaxNameLen+2-$len));
X      }
X   } else {                     # a continuation
X      push(@Group,@list);       # add to the group list
X   }
X}
X$OutCount += do PrintGroup() if $#Group >= $[;
Xclose NETGRPIN;
Xclose NETGRPOUT;
Xif (!$Debug && $> == 0) {	# if root and not debug
X   unlink "$NetGrpOut.old"		if -e "$NetGrpOut.old";
X   rename($NetGrpOut,"$NetGrpOut.old")	if -e $NetGrpOut;
X   rename("$NetGrpOut.tmp",$NetGrpOut);
X}
Xprintf "%d groups in; %d groups out.\n", $InCount, $OutCount if $Verbose;
Xexit 0;
X
X# This subroutine prints the current accumulated group list
X# in "grp".
X# Return the # of groups printed.
X
Xsub PrintGroup {
X   local($grplen);
X   local($grpname) = shift(@Group);
X   local($firstgrp) = $grpname;
X   local($line);
X   local($member);
X   local($grpcount) = 1;
X   local($totalgrplen) = 0;
X   local($incname) = '01';
X
X   $line = "$grpname\t";
X   $grplen = length($line);
X   foreach $member (@Group) {
X      if ((length($line) + length($member) + 1) >= ($MAXLINELEN - 8)) {
X         print NETGRPOUT $line . "\\\n";
X         $grplen += length($line) + 2;
X         $line = "\t";
X      }
X      if ($grplen >= ($MaxRecLen - (length($grpname) + 4))) {
X	 local($newgrpname) = $grpname;
X	 $newgrpname .= $incname if $grpname !~ /\d$/;
X	 $newgrpname = $firstgrp . ++$incname 
X	    while ($NetGroupNames{$newgrpname});
X	 $grpname = $newgrpname;
X	 $NetGroupNames{$grpname} = 1;
X	 print NETGRPOUT $line . $grpname . "\n";
X	 $line = "$grpname\t";
X	 $totalgrplen += $grplen + length($grpname) + 1;
X	 $grplen = length($line);
X	 $grpcount++;
X      }
X      $line .= $member . ' ';
X   }
X   print NETGRPOUT ($line . "\n");
X   if ($Verbose) {
X      $totalgrplen += $grplen + length($line) + 1;
X      printf "%3d members", ($#Group + 1 - $[);
X      printf ", %2d subgroups", ($grpcount-1) if $grpcount > 1;
X      printf ", %4d bytes.\n", $totalgrplen;
X   }
X   return $grpcount;
X}
END_OF_mknetgrp
if test 6201 -ne `wc -c <mknetgrp`; then
    echo shar: \"mknetgrp\" unpacked with wrong size!
fi
chmod +x mknetgrp
# end of overwriting check
fi
if test -f mknetgrp.8 -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"mknetgrp.8\"
else
echo shar: Extracting \"mknetgrp.8\" \(2915 characters\)
sed "s/^X//" >mknetgrp.8 <<'END_OF_mknetgrp.8'
X.TH MKNETGRP 8 "October 10, 1988"
X.UC
X.SH NAME
Xmknetgrp \- will parse the netgroup input file and write a source
Xwith suitable record sizes for parsing by "makedbm" (part of YP).
X.SH SYNOPSIS
X.B mknetgrp
X[
X.B \-v
X]  [
X.B \-d
X]  [
X.B \-i 
Xin ]  [
X.B \-o 
Xout ]  [
X.B \-l 
Xmaxrec ]
X.SH DESCRIPTION
X.I mknetgrp
Xparses each netgroup record, and if it finds a large record, it will
Xbreak it up into smaller pieces with sequentially named subgroups.
XThe reason for doing this is because of bugs in the dbm(3) and ndbm(3)
Xwhich limit record sizes to 1024 (or 4096 for ndbm) characters.
X.LP
XBy default, the netgroup input file is /etc/netgroup.master, while
Xthe netgroup output file is /etc/netgroup.  These can be changed
Xwith the -i and -o options.
X.LP
XWhen editing a netgroup file manually or with addnetgrp(8), the record
Xsize may exceed the limitations that makedbm can handle.  Running
X.I mknetgrp
Xafter changing a netgroup file, will correct any problems with record
Xsize limitations. See the manual pages for "netgrp" and addnetgrp to
Xget an idea of how the netgroup file is set-up.
X.SH OPTIONS
X.TP
X.B \-i in
XChange the input file that is being parsed for netgroup
Xlengths.  Without this option, the default input netgroup file
Xis /etc/netgroup.master.
X.TP
X.B \-v
XInitiate a verbose mode of what
X.I mknetgrp
Xis doing.
X.TP
X.B \-d
XUsed to set the debug flag ?? Seems to me that this is
Xmore of a default flag setter. See mknetgrp for what this
Xoption does.
X.TP
X.B \-l maxrec
XUsed to set the maximum length of a netgoup's record size.  
XBy default, the maximum record length has been set for
X4095 characters (suitable for ndbm).  If you require
Xsmaller record sizes for parsing, you must use this option.
X.TP
X.B \-o out
XChange the output netgroup file that has been parsed into
Xnetgroups with suitable lengths.  Without this option, the
Xdefault output file is /etc/netgroup.
X.TP
X.SH EXAMPLES
X.LP
XThe first example adds the tuples (,tom,) (,dick,) (,harry,)
Xto the netgroup "cs-staff" in the default netgroup file
X(/etc/netgroup.master).
X.LP
XThe second example removes the tuples (goofy,,) (mickey,,)
Xfrom the netgroup "csil-clients" in the specified netgroup file 
X(/etc/netgroup.orig). It also displays messagges about the
Xtuples that were added to the netgroup.
X.LP
XThe third example does not add tuples, but adds the netgroups
Xnew-clients and old-clients themselves to the netgroup called
Xcs-clients.
X.LP
XThe fourth example removes the tuples (,minnie,) (,daffy,)
Xfrom the netgroup cs-faculty without complaining about 
Xusuernames not in that particular netgroup.
X.IP
Xaddnetgrp cs-staff tom dick harry
X.br
Xaddnetgrp -rvh -F /etc/netgroup.orig csil-clients goofy mickey
X.br
Xaddnetgrp -vn cs-clients new-clients old-clients
X.br
Xrmnetgrp -uf cs-faculty minnie daffy
X.SH FILES
X/etc/netgroup.master 	default netgroupfile.
X/etc/netgroup		system netgroupfile.
X.SH SEE ALSO
Xnetgroup(5), mknetgrp(8)
X.SH BUGS
XNone reported.
END_OF_mknetgrp.8
if test 2915 -ne `wc -c <mknetgrp.8`; then
    echo shar: \"mknetgrp.8\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of archive 1 \(of 1\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 1 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0

jmm@eci386.uucp (John Macdonald) (12/10/90)

In article <1990Dec4.225525.15948@uvaarpa.Virginia.EDU> aks@hub.ucsb.edu writes:
|| is there some arbitrary ceiling on the number of machines one can
|| define for membership in a netgroup id in the /etc/netgroup file ??  I
|| have evidence that there is some limit.  Would like to know if others
|| have similar suspicions.
|
|Yes.  If you are using the older YP routines, they use "dbm" maps, which
|have an internal limit of 1024 characters per record.  Even the newer
|versions, using "ndbm", still have the internal limit, it's just a
|little bigger, at 4096 bytes per record.

As an alternative to dbm or ndbm, you can link in Ozan Yigit's sdbm
routines.  They provide a superset of ndbm for calling interface,
run faster, and don't limit the size of either individual elements
or groups of elements that have the same hash code.  It uses a different
hash function (and provides a straightforward hook for you to provide
your own [via recompiling] if you have odd data that pashes poorly).
Oh, and it has no restrictions on usage.  Go to his presentation in
Houston.

For ease of converting, I wrote a simple perl program that can turn
a dbm file into a binary dump and vice versa, you just run it using
a version of perl linked with the appropriate dbm routines in each
case - e.g. "operl dbdump olddata | nperl dbgen newdata".

Here it is...

----- cut here for dbtool - dbdump/dbgen/dbdiff/dbincl -----
sub usage {
    print "usage: perl dbtool -dump|-diff|-gen|-incl dbm-file\n";
    print "   or: perl dbdump dbm-file\n";
    print "   or: perl dbdiff dbm-file\n";
    print "   or: perl dbgen  dbm-file\n";
    print "   or: perl dbincl dbm-file\n";
    exit(1);
}

if( $0 =~ m/dbtool$/ ) {
    $0 = shift;
    $0 =~ s/^-/db/;
}

if( $0 =~ m/db(dump|diff|gen|incl)$/ ) {
    $proc = "do$1";
} else {
    &usage();
}

if( $#ARGV != 0) {
    &usage();
}

dbmopen( DB, $ARGV[0], 0644 );

do $proc();

dbmclose( DB );

sub dodump {
    while( ($key,$val) = each %DB ) {
	printf( "K%8d", length($key) );
	print $key;
	printf( "V%8d", length($val) );
	print $val;
    }
}

sub dodiff {
    while( ($key,$val) = each %DB ) {
	$recnum++;

	$stdkey = &getstr( 'K' );
	$stdval = &getstr( 'V' );

	if( $stdkey != $key ) {
	    die "key mismatch in record $recnum\ndb file key:<$key>\nstdin    key:<$stdkey>\n";
	}
	if( $stdval != $val ) {
	    die "val mismatch in record $recnum\ndb file val:<$val>\nstdin    val:<$stdval>\n";
	}
    }
}

sub doincl {
    for(;;) {
	$stdkey = &checkifstr( 'K' );
	exit if chop($stdkey) eq 'E';
	$stdval = &getstr( 'V' );

	if( !defined($DB{$stdkey}) ) {
	    die "key not found:<$stdkey>\n";
	}
	if( $DB{$stdkey} ne $stdval ) {
	    die "value mismatch\n  key read: <$stdkey>\n  val read: <$stdval>\n  val found: <$DB{$stdkey}>\n";
	}
    }
}

sub dogen {
    for(;;) {
	$stdkey = &checkifstr( 'K' );
	exit if chop($stdkey) eq 'E';
	$stdval = &getstr( 'V' );

	$DB{$stdkey} = $stdval;
    }
}

sub getstr {
    local($reqcode) = @_;
    local($code);

    if( read(STDIN,$code,1) != 1 ) {
	die "unexpected EOF at record $recnum";
    }
    if( $code != $reqcode ) {
	die "out of sync at record $recnum, expected $reqcode, got $code";
    }
    &getrest();
}

sub checkifstr {
    local($code);

    if( read(STDIN,$code,1) == 1 ) {
	if( $code != 'K' ) {
	    die "out of sync at record $recnum, expected K, got $code";
	}
	&getrest() . 'K';
    } else {
	'E';
    }
}

sub getrest {
    local($strlen,$result);

    if( read(STDIN,$strlen,8) != 8 ) {
	die "unexpected EOF at record $recnum";
    }
    if( read(STDIN,$result,$strlen+0) != $strlen+0 ) {
	die "unexpected EOF at record $recnum";
    }
    $result;
}
-- 
Cure the common code...                      | John Macdonald
...Ban Basic      - Christine Linge          |   jmm@eci386