rogers (06/12/82)
: run this file as a shell script mkdir groups.src cd groups.src echo "Extracting README..." cat > README <<'E*O*F' Information regarding the groups command: Groups is a C program designed to provide the user with a list of the valid groups which he may newgrp to. There are several options available, and the user may find out the group names other users are in. USAGE: groups [-ncdxl] [user user ...] Here is a brief summary of the options: none: Provides list of gid names only. -n: Gives the addtional group id number -c: Gives the user's current group. -d: Gives the user's default group. -x: Expands the group name (see below) -l: Provides all the above functions. Options may be combined, of course, to get desired results. IF THE FILE /etc/groupnames is not present, you may want to add this file so that groups will do the expansion of the groupnames. The program will run fine without ti though. HERE IS THE FORMAT OF /etc/groupnames recognized by groups: grnam:gid:longname[;more info if desired]\n where : grnam is the name of the group as in group(5), gid is the group id number as in group(5), longname is ascii text describing the group (also can contain other info after the semicolon if desired. \n is a newline to terminate the line between entries. After the make has completed it's appointed task, The following should happen: Test the groups command by running the executable with each option seperately, and then combine options. Test the 'user' option by adding the name of several users to the end of the groups invoking. Move the file groups to /usr/bin or equivalent. Move the file groups.1 to /usr/man/man1 or equivalent, and test out the manual page (man groups). GOOD LUCK!!!!!!! Roger Southwick. E*O*F echo "Extracting Manual Page..." cat > groups.1 <<'E*O*F' .TH GROUPS 1 "6/12/82" .SH NAME groups \- give valid group names for current user. .SH SYNOPSIS .B groups [\-ncdxl] [user user ...] .SH DESCRIPTION .I Groups gives a list of valid group names to which the user may change to by using a newgrp command. By using the option flags, the user may get the following additional data: .IP \-n : Provides the current group number. .IP \-c : Provides the current group name (as in whoami(1)). .IP \-d : Provides the user's default group name. .IP \-x : Expands the group name for more description. .IP \-l : Provides all the above options. .IP user : Provides the group names for the user(s) listed. This has the effect of turning off the -c and -d options. .LP The options may be combined as in: .IP groups -x -n .LP \-or\- .IP groups -xn .SH FILES USED /etc/group \- to get the user names and group names. /etc/groupnames \- to get the group name expansions. .SH NOTES If /etc/groupnames is not on the system, the -x and -l option will not provide group name expansion. .LP The effective user name is used, so if you are su'd, you will get the group names for the current user name. .SH AUTHOR Roger Southwick Tektronix, Inc. P.O. Box 500, D.S. 92-525 Beaverton, Oregon 97077 .SH "SEE ALSO" newgrp(1), whoami(1) .SH BUGS To be discovered. E*O*F echo "Extracting code ..." cat > groups.c <<'E*O*F' /*************************************************************** groups By: Roger Southwick Tektronix, Inc. P.O. Box 500, D.S. 92-525 Beaverton, Oregon 97077 Date : 6/10/82 This program is designed to do the groups command. This command provides the user with the valid groupnames he may newgrp to. Using the option flags, the user may get different other info (described below). The user may also stack arguments together for even more into. USAGE: groups [-ncdxl] [user user ...] The flags allowed are: none : This provides a list of the gid names only. -n : This provides the addtional group id number. -c : This provides the current group name (as in whoami). -d : This provides the user's default group name. -x : This provides the expansion of the gid names into the long name. (This works only if /etc/groupnames is present). -l : This provides the user with all of the functions. (This works only if /etc/groupnames is present). user : The groups function will also print out the group names for each user name specified, but this also has the effect of turning off the -c and -d options. --------------------------------------------------------------- Files used: /etc/group - provides the names of all the groups the user is valid to newgrp to. /etc/groupnames - provides the expansion for the names. (This file is optionally not in unix, so is not required, but option -l & -x won't work without it.) <grp.h> - provides the needed mold for getgrgid & getgid. <pwd.h> - mold for getpwuid and getuid. ------------------------------------------------------------------ MODIFICATION HISTORY: 6/11/82 - Changed the pnode structure so it has different names than the tnode structure (left becomes lft, right becomes rght). Makes program portable. 6/11/82 - Added the define for NCMP. This change allows the makes the string comparisons only look at this many characters. This is used in all strncmp's. 6/11/82 - Added the knowuser function. This checks the user names (entered as parameters especially) to be valid user names or not. This can be disabled by removing the #define KNOWENABLE statement (or commenting it out) in the defines section. ********************************************************************/ #include <stdio.h> /*----------------------------------------------------------- Structure pnode holds the name of a user which is allowed in the group. The group structure, tnode, points to this structure at the name of the first user in the group. -----------------------------------------------------------*/ struct pnode { char *name; /* point to a user's name */ struct pnode *lft; /* left child */ struct pnode *rght; /* right child */ }; /*----------------------------------------------------------- Structure tnode holds a group name, the gid number, the group long name (if available), and the pointer to the first user (pnode) in the group -----------------------------------------------------------*/ struct tnode { char *grpnm; /* point to the group name */ int gid; /* hold the gid number */ char *longname; /* hold the expanded group name */ struct pnode *frst; /* point to the first person in the pnode */ struct tnode *left; /* left child */ struct tnode *right; /* right child */ }; /*----------------------------------------------------------- Structure helpmsg (init'ed into stuf), holds the help messages. -----------------------------------------------------------*/ struct helpmsg { char *msgstr; }stuf[] = { "none : Provides a list of the gid names only.", " -n : Provides group id number.", " -c : Provides current group name (as in whoami).", " -d : Provides default group name.", " -x : Expands of the gid names into long name.", " -l : Invokes all of the functions.", "Flags may be combined to give desired results.", "Other user names may be added, this turns off -d & -c " }; #define NHLPMSGS (sizeof(stuf) / sizeof(struct helpmsg)) main(argc,argv) int argc; /* number of arguments */ char *argv[]; /* pointers to the actual arguments */ { char *s, *username(); struct tnode *stash(), *pntr; FILE *fp, *fgn, *fopen(); int gnum = 0; /* put out gid num if set to 1 */ int cnam = 0; /* put out current group name if 1 */ int defgp = 0; /* put out default group name if 1 */ int xpnam = 0; /* expand names if set to 1 */ /*----------------------------------------------------------- Now process the arguments. Set the appropriate flag to invoke the userpr afterwards -----------------------------------------------------------*/ while(--argc > 0 && (*++argv)[0] == '-') for (s = argv[0]+1; *s != '\0'; s++) switch (*s) { case 'n' : /* give gid number */ gnum = 1; break; case 'c' : /* give current group */ cnam = 1; break; case 'd' : /* give default group */ defgp = 1; break; case 'x' : /* expand the names */ xpnam = 1; break; case 'l' : /* give all the above */ gnum = 1; cnam = 1; defgp = 1; xpnam = 1; break; default : /* opps! bad flags */ help(); exit(1); break; } /*-------------------------------------------------------------- Now open up the files: /etc/group & /etc/groupnames. It is an error if /etc/group can't be opened, but not an error if /etc/groupnames can't be opened (this file is not std unix, so we must make allowances). --------------------------------------------------------------*/ if((fp = fopen("/etc/group","r")) == NULL){ fprintf(stderr,"groups: can not open /etc/group\n"); exit(1); } else { fgn = fopen("/etc/groupnames","r"); /*-------------------------------------------------------------- Now to finally do the print out. This is done by calling userpr with the top of the tnode tree (supplied by stash), the name of the user (supplied by username, which also prints out the header), the flags gnum and xpnam for expanding the printout. We then call up the functions curname (if cnam is set) and defgrp (if defgp is set). If there are more arguments on the line, assume they are user names, which it is desired to give groups for. In this case, we keep doing headers for each name (which must be known). When getting groupnames for users OTHER THAN the logged in user, we cannot do the current or default group printouts. --------------------------------------------------------------*/ pntr = stash(fp,fgn); /* init the trees */ if(argc == 0){ /* if no users named... */ header(username()); /* use the current user */ userpr(pntr,username(),gnum,xpnam); } else { /* use args as user names */ defgp = cnam = 0; /* turn off options */ while (argc-- > 0) if(knowuser(pntr,*argv) == 1){ /* see if user is known */ header(*argv); userpr(pntr,*argv++,gnum,xpnam); } else /* we don't know this user */ fprintf(stderr,"\ngroups: Unknown user: %s\n",*argv++); } putchar('\n'); /* add a <cr> */ if(cnam == 1) curname(); if(defgp == 1) defgrp(); if(cnam == 1 || defgp == 1) printf("\n\n"); /* add 2 <cr>'s */ exit(0); } } /*-------------------------------------------------------------- Function help: This provides the usage message and a brief description of each flag. --------------------------------------------------------------*/ help() { int i; fprintf(stderr,"\nUsage: groups [-ncdxl] [user user ...]\n\n"); for(i = 0; i < NHLPMSGS ; i++) fprintf(stderr,"%s\n\n",stuf[i].msgstr); } /*-------------------------------------------------------------- Format notes: The format for the file /etc/group is given in group(5) of the V7 unix manual. The format assumed for /etc/groupnames is: grpnm:gid:longname[;other info] where: grpnm - is the group name as in group(5), gid - is the group number, as in group(5), longname - is the long group name (ascii text describing the group name (grpnm). --------------------------------------------------------------*/ /*-------------------------------------------------------------- Globally available variables and constants --------------------------------------------------------------*/ #define MAXLINE 10000 /* Maximum number of chars in a line of group */ #define EOL '\012' /* end of line character in groups and groupnames */ #define NAMESIZE 10 /* the maximum length of a user's name */ #define GNAME 20 /* the maximum length of a group name */ #define LNAME 50 /* the maximum length of a long grp name */ #define GIDLEN 5 /* maximum length of the gid number string */ #define TAB 15 /* the place to tab out to after group name */ #define NCMP 8 /* # of chars compared between groupnames & group */ #define KNOWENABLE /* remove if you do not want to test params */ int cnt; /* the count of letters done in a line */ char line[MAXLINE]; /* the line of text read */ /*-------------------------------------------------------------- Function stash: returns a pointer to the top of the tnode tree. Reads lines of fp (/etc/group) and places the group name, gid number, and user names into tnode tree by hashing these values out, and calling function tree. The longnames are hashed out of fgn (/etc/groupnames, if available) when all the group names are done (/etc/group fully read out), and added to the tnodes with calls to addlong function. --------------------------------------------------------------*/ struct tnode *stash(fp,fgn) FILE *fp,*fgn; { struct tnode *top,*tree(); char gname[GNAME]; char longnm[LNAME]; char gidstr[GIDLEN]; int gid,n; top = NULL; while (fgets(line,MAXLINE,fp)!= NULL){ /* read a line of input */ for(cnt = 0; line[cnt] != ':';cnt++) /* get group name place into gname */ gname[cnt] = line[cnt]; gname[cnt++] = '\0'; /* add a null */ for( ; line[cnt] != ':'; cnt++) /* throw away group passwd */ ; for(++cnt,n = 0; line[cnt] != ':';cnt++,n++) /* get the gid number */ gidstr[n] = line[cnt]; gidstr[++n] = '\0'; /* add a null */ gid = atoi(gidstr); /* convert to a number */ top = tree(top,gname,gid); } /*--------------------------------------------------------- Now add the expanded groupnames to the list, if file fgn (/etc/groupnames) is available. ---------------------------------------------------------*/ if(fgn != NULL){ while (fgets(line,MAXLINE,fgn) != NULL){ for(cnt = 0;line[cnt] != ':';cnt ++) /* grab groupname */ gname[cnt] = line[cnt]; gname[cnt++] = '\0'; /* add a null */ for( ; line[cnt] != ':';cnt++) /* discard gid number */ ; /* now grab the long name */ for(++cnt,n = 0; line[cnt] != EOL && line[cnt] != ';';cnt++,n++) longnm[n] = line[cnt]; longnm[n] = '\0'; /* add a null */ addlong(top,gname,longnm); /* call addlong to install */ } } return(top); /* go back to calling program (main) */ } /*--------------------------------------------------------- Function tree provides a recursive method of storing the group name (nm) at node p or below. Also stores the gid number. Then it gets one user name at a time and calls ptree to install the user name in the associated pnode tree. ---------------------------------------------------------*/ struct tnode *tree(p,nm,gid) struct tnode *p; char *nm; int gid; { struct tnode *talloc(); char *strsave(),name[NAMESIZE]; struct pnode *ptree(); int n; if (p == NULL) { /* a new node is desired */ p = talloc(); /* make a new node in main memory */ p->grpnm = strsave(nm); /* save the group name */ p->gid = gid; /* save the gid number */ p->longname = NULL; /* make longname point nowhere */ p->left = p->right = NULL; /* init pointers */ p->frst = NULL; while(line[cnt] != EOL){ /* get a person's name */ cnt++; /* increment past , */ /* grab the person's name */ for(n = 0;line[cnt]!=',' && line[cnt]!=EOL;n++,cnt++) name[n] = line[cnt]; name[n] = '\0'; /* add a null */ p->frst = ptree(p->frst,name); /* save the name */ } } else if (strncmp(nm,p->grpnm,NCMP) <= 0) /* goes in left tree */ p->left = tree(p->left,nm,gid); else p->right = tree(p->right,nm,gid); /* goes right */ return(p); /* return pointer to tnode */ } /*--------------------------------------------------------- Function ptree installs the user name (name) in the pnode tree at node p or below, by use of recursion. ---------------------------------------------------------*/ struct pnode *ptree(p,name) struct pnode *p; char *name; { struct pnode *palloc(); char *strsave(); if(p == NULL) { /* make a new node */ p = palloc(); /* create a new node in memory */ p->name = strsave(name); /* save the name of the user */ p->lft = p->rght = NULL; /* init the pointers */ } else if(strncmp(name,p->name,NCMP) <= 0) /* goes in left tree */ p->lft = ptree(p->lft,name); else /* goes in right tree */ p->rght = ptree(p->rght,name); return(p); /* return pointer to pnode */ } /*---------------------------------------------------------------- Add the longname lnm to the structure tnode for each groupname This is done by looking up the gid name nm in the list, and adding the lnm to p->longname. ----------------------------------------------------------------*/ addlong(p,nm,lnm) struct tnode *p; char *nm,*lnm; { int cmp; if(p != NULL){ /* Don't add the long name if no short match */ if((cmp = strncmp(nm,p->grpnm,NCMP)) == 0) /* found a match */ p->longname = strsave(lnm); else if(cmp < 0) /* go left */ addlong(p->left,nm,lnm); else /* go right */ addlong(p->right,nm,lnm); } } /*-------------------------------------------------------------- Memory allocation routine: talloc. This provides main memory for a tnode by doing a cast of the main memory allocator malloc. It returns a pointer to the new node out in memory. --------------------------------------------------------------*/ struct tnode *talloc() { return((struct tnode *) malloc(sizeof(struct tnode))); } /*-------------------------------------------------------------- Memory allocation routine: palloc. This provides main memory to a pnode by doing a cast of the main memory allocator malloc. It returns a pointer to the new node out in memory. --------------------------------------------------------------*/ struct pnode *palloc() { return((struct pnode *) malloc(sizeof(struct pnode))); } /*-------------------------------------------------------------- The strsave routine allocates some main memory for the string s, copys the string there and returns a pointer to the place in memory --------------------------------------------------------------*/ char *strsave(s) char *s; { char *p; p = (char *)malloc(strlen(s) +1); strcpy(p,s); return(p); } /*-------------------------------------------------------------- Print out the group names for the user: name. This is done with recursive calls to userpr, which transverses the tnode tree, looking for the user name with the function: namein (below). The info is printed out according to the parameters gnum (add the group id number) and xpnam (add the expanded group name) --------------------------------------------------------------*/ userpr(p,name,gnum,xpnam) struct tnode *p; char *name; int gnum, xpnam; { int namein(); int len; /* the length of the group name */ if (p != NULL) { /* only print out if not a NULL node */ userpr(p->left,name,gnum,xpnam); /* go all the way left */ if(namein(p->frst,name)){ /* found a match */ len = strlen(p->grpnm); /* get the string length */ printf("%s",p->grpnm); /* print group name */ for( ; len <= TAB ; len ++) /* add spaces to TAB */ putchar(' '); if(gnum == 1) /* print out gid number */ printf("%d",p->gid); if(xpnam == 1) /* add expanded name */ if(p->longname != NULL) /* only if there, tho */ printf("\t%s",p->longname); putchar('\n'); /* add <cr> */ } userpr(p->right,name,gnum,xpnam); /* go down the right tree */ } } /*-------------------------------------------------------------- Function knowuser returns a 1 if the user name: name is anywhere in any of the pnode trees. This function could be done without (since it takes time to check), and can be quickly disabled by removing the #define KNOWENABLE in the defines section. --------------------------------------------------------------*/ int status = 0; /* the status variable */ int knowuser(p,name) struct tnode *p; char *name; { #ifdef KNOWENABLE if(p != NULL) { /* don't do anything for a NULL node */ knowuser(p->left,name); /* go all the way left */ if(namein(p->frst,name)) /* test for user in this group */ status = 1; knowuser(p->right,name); /* go all the way right */ } #else status = 1; /* if not enabled, we know invalid users */ #endif return(status); } /*-------------------------------------------------------------- Function namein returns a 1 if user name: name is in the pnode tree pointed to by p. This is done by recursively calling namein looking for a match. --------------------------------------------------------------*/ int namein(p,name) struct pnode *p; char *name; { int cond; if(p == NULL) /* End of tree and name not found */ return(0); else { if((cond =strncmp(name,p->name,NCMP)) == 0) /* test name for match */ return(1); else if (cond < 0) /* go left if less than */ return(namein(p->lft,name)); else /* go right */ return(namein(p->rght,name)); } } /*----------------------------------------------------------- This function provides the current group name for the user. ------------------------------------------------------------*/ #include <grp.h> struct group *gp; struct group *getgrgid(); curname() { gp = getgrgid(getgid()); printf("Current group is: %s\t",gp->gr_name); } /*----------------------------------------------------------- This function returns a pointer to current user's name. ------------------------------------------------------------*/ #include <pwd.h> struct passwd *getpwuid(); struct passwd *pp; char *username() { pp = getpwuid(getuid()); return(pp->pw_name); } /*----------------------------------------------------------- This function prints a nice looking header for user name nm -----------------------------------------------------------*/ header(nm) char *nm; { printf("\nValid groups for %s:\n",nm); } /*----------------------------------------------------------- This function prints out the user's default groupname ------------------------------------------------------------*/ defgrp() { pp = getpwuid(getuid()); gp = getgrgid(pp->pw_gid); printf("Default group is: %s",gp->gr_name); } E*O*F echo "Extracting makefile..." cat > makefile <<'E*O*F' FILES = /usr/include/stdio.h /usr/include/grp.h /usr/include/pwd.h /etc/group groups : groups.c $(FILES) cc -O -o groups groups.c E*O*F echo "Running Makefile..." make