[net.sources] chall, recursive chown, chgrp

adams (04/23/83)

#N:uiucdcs:12600008:000:8218
uiucdcs!adams    Apr 22 17:58:00 1983


	What follows is the source and man page (in a Bourne RunMe script)
for chall, a recursive chown/chgrp.  Syntax is as follows:

% chall owner group dir1 dir2 ...
E.g.,
% chall uucp bin /usr/lib/uucp /usr/spool/uucp

	The example appropriately changes the ownership (and group ownship)
of /usr/lib/uucp, /usr/spool/uucp and all of their descendants.

	For more info, read the man page.

	Instructions:
		Save this in a file, and get rid of this
		garbage at top

		type /bin/sh RunMe

		type make chall

	Known lacks:
		Reading the directory could be more efficient. 
		(E.g. I could use 4.1aBSD's directory readers, which
		has the side effect of making this work for 4.1c)
		N.b. I don't know if this will work on 4.1c.

		Error recovery is less than gracious.  If ANYTHING goes
		wrong, chall just dies (by design).  I prolly ought
		to have it continue its task.

		It has only been tested on 4.1,4.1aBSD, and one hybrid
		UNIX running on a Cadlinc Sun 68000.  If it does
		not port easily to your machine, please let me know.

		I have one open file for every directory level.  This means
		that you may only go (NOFILE -3) levels deep.  On my machine
		this means I can get to *almost* everything if I execute
		%chall adams bin /   (No, I didn't try it...)

	RCS:
		I use purdue's RCS (Revision Control System), and it
		has left its mark in my code.  To get RCS, contact
		purdue!wft  or Tichy@purdue.

	Distribution:
		I don't care where it goes, but don't sell it, and if
		you find any bugs (in my code?), let me know.


					Rob Adams
					(217) 333 3536
					uiucdcs!adams
	By the way:
		There is a reason I don't just use find(1).  This
		runs (seriously) 100 times as fast.  Really.
============================ RunMe Follows... ========================
# This is the RunMe script for chall, from the University of Illinois
# This Software may be distributed provided 
#	1) It may NOT be sold.
#	2) The recipient agrees to (1).
#
#			Rob Adams
#			decvax!pur-ee!uiucdcs!adams
#			parsec!uiucdcs!adams
#			UofIllinois
echo Extracting Makefile
cat << !EOF!EOF! > Makefile
chall: main.o walk.o assert.o
	cc -o chall main.o walk.o assert.o
clean:
	rm -f *.o  chall core
!EOF!EOF!
echo Extracting main.c
cat << !EOF!EOF! > main.c
static char *RCSid = "$Header: /mnt/staff/adams/Cprogs/Chall/RCS/main.c,v 1.5 83/04/21 07:13:03 adams Exp $";

/* Modification history --
 *
 * $Log:	/mnt/staff/adams/Cprogs/Chall/RCS/main.c,v $
 * Revision 1.5  83/04/21  07:13:03  adams
 * Added multiple directories.
 * 
 * Revision 1.4  83/04/21  06:59:56  adams
 * indented right.
 * R
 * 
 * Revision 1.3  83/04/11  04:52:30  adams
 * Added su-checking.
 * 
 * Revision 1.2  83/04/11  04:47:38  adams
 * changed all the asserts to my new format.
 * 
 * Revision 1.1  83/04/11  03:30:24  adams
 * Initial revision
 * 
 *
 */

/* chall -- change the ownership and group ownership of a directory
 * and all its descendants.  (I wonder if that is spelled right.. hmmm)
 */
/*
 *
 *  The line marked $Header: and the comment marked $Log: are courtesy
 *  RCS, the Revision Control System written and distributed by 
 *  Walter F. Tichy at University of Purdue.  I have found it invaluable
 *  in generating code, and recommend it whole-heartedly.  For more
 *  information, contact the author.  (purdue!wft or Tichy@purdue)
 * 
 */

/* 
 * original work by Rob Adams, (uiucdcs!adams) on 11 April.
 *
 */
#include <stdio.h>
#include <pwd.h>
#include <grp.h>

main (argc, argv)
int     argc;
char   *argv[];


{

 /* some defs here later. */
    int     uid,
            gid;
    int     i;
    int     walked;
    struct passwd  *pswd,
                   *getpwnam ();
    struct group   *grp,
                   *getgrnam ();


 /* Were we called correctly? */
    assert ((argc >= 4), 's', "Usage: chall owner group file");

 /* Am I su?  non-roots fail */
    assert (((getuid () == 0) || (geteuid () == 0)), 's', "You must be SU to run chall");


 /* parse the args, first the name (argv[1]) */

    setpwent ();
    pswd = getpwnam (argv[1]);
    endpwent ();
    assert ((pswd != (struct passwd *) 0), 's', "Cant get user info");
    uid = pswd -> pw_uid;

 /* now comes the group. */

    setgrent ();
    grp = getgrnam (argv[2]);
    endgrent ();
    assert ((grp != (struct group  *) 0), 's', "Cant get group info");
    gid = grp -> gr_gid;

 /* now, lets just pass everything off to the tree walker. */

    for (i = 3; i < argc; i++)
    {
	walked = walk (uid, gid, argv[i]);
	assert (walked == 0, 's', "Something is wrong, but I don't know what");
    }
    exit (walked);			  /* I know, walked is guaranteed to be 0 here */
}
!EOF!EOF!
echo Extracting walk.c
cat << !EOF!EOF! > walk.c
static char *RCSid = "$Header:   RCS/walk.v  Revision 1.2  83/04/11  04:48:28  adams  Exp$";

/*
 * walk()
 *	    This guy does all the recursion.  It should propagate
 *	    errors backwards, but I don't know if I will do that tonight.
 *
 */
#include <sys/types.h>
#include <sys/dir.h>
#include <sys/stat.h>
#include <stdio.h>

walk (uid, gid, filename)
int     uid,
        gid;
char   *filename;
{
    char    file_nullend[2 * DIRSIZ];	  /* better safe, et al... */
    struct stat status;
    struct direct   dirbuf;
    long lseek();	/* for lint */
    int     stated;
    int     isdir;
    int     walked;
    long lseeked;
    int     chowned = 0;
    int     dot;
    int     readed;

 /* see if filename is a non-directory or directory */

    stated = stat (filename, &status);
    assert ((stated == 0),'p',"chall: stat()");
    isdir = ((status.st_mode & S_IFMT) == S_IFDIR);
    if (!isdir)				  /* I know, I really don't need
					     isdir, but I like it */
    {
	chowned = chown(filename, uid, gid);
	assert ((chowned == 0),'p',"chall: chown()");
	return (0);
    }
 /* else */
    chdir (filename);
	chowned = chown(".", uid, gid);
	assert ((chowned == 0),'p',"chall: chown()");
    dot = open (".", 0);		  /* 0 means read-only */
    assert ((dot != (-1)),'p', "chall: read()");
    lseeked = lseek (dot, (long) 2*sizeof (dirbuf),0);
    assert ((lseeked != (-1)),'p',"chall: lseek()");		/* blow off . and .. */
    while (1 == 1)
    {					  /* I suppose I should make a
					     condition in there, but I am
					     lazy.. */

	readed = read (dot, (char *) & dirbuf, sizeof (dirbuf));
	if (readed == 0)		  /* EOF */
	    break;
	assert ((readed == sizeof (dirbuf)),'p', "chall: read()");
	if (dirbuf.d_ino == 0)
	    continue;
    /* I need to terminate filename with a null char. */
	(void) strncpy (file_nullend, dirbuf.d_name, DIRSIZ + 1);
	walked = walk (uid, gid, file_nullend);
	assert ((walked == 0),'s',"Something mysterious just happend");
    }
    close (dot);
    chdir ("..");
    return (0);				  /* just emptied a directory */
}
!EOF!EOF!
echo Extracting assert.c
cat << !EOF!EOF! > assert.c
static char *RCSid = "$Header:   RCS/assert.v  Revision 1.2  83/04/11  04:47:02  adams  Exp$";

/*
 * assert(bool,char,char *)
 *
 * 	program verification...
 *
 *	If bool is true then return, else print a message and exit(-1)
 *	
 *	If char = p then perror(3) is used. 
 *
 */


#include <stdio.h>

assert (bool, c, s)
int     bool;
char    c;
char   *s;
{
    if (bool)
	return;
    if (c == 'p')
	perror (s);
    else
	fprintf (stderr, "%s\n", s);
    exit (-1);
}
!EOF!EOF!
echo Extracting man'(8)' page
cat << !EOF!EOF! > chown.8
.TH CHOWN 8 
.UC 4
.SH NAME
chown, chgrp, chall \- change owner or group
.SH SYNOPSIS
.B /etc/chown
owner file ...
.PP
.B /etc/chgrp
group file ...
.PP
.B chall owner group directory ...
.SH DESCRIPTION
.I Chown
changes the owner of the
.I files
to
.IR owner .
The owner may be either a decimal UID or
a login name found in the password file.
.PP
.I Chgrp
changes the group-ID of the
.I files
to
.IR group .
The group may be either a decimal GID or
a group name found in the group-ID file.
.PP
.I Chall
changes the owner and group, of any file in a directory and
its subdirectories. The owner and group must be logical names,
not decimal UID or GID's.
.PP
Only the super-user can change owner or group,
in order to simplify as yet unimplemented accounting procedures.
.SH FILES
/etc/passwd
.br
/etc/group
.SH "SEE ALSO"
chown(2),
passwd(5), group(5)
!EOF!EOF!

rusty (04/24/83)

From: rusty
i find your excuse for not using find pretty feeble.
how often does one need to do what chall does? i've
used find to do this several times, i'd say at most
on the average of once every 4 months. hardly seems
worth the trouble of keeping around an extra program
and its manual page, every time i'd want to use chall
i'd have to read its manual page to remind me how it
works.