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.