xref: /onnv-gate/usr/src/cmd/users/users.c (revision 549:9e644232f978)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
220Sstevel@tonic-gate 
230Sstevel@tonic-gate /*
240Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
250Sstevel@tonic-gate  * Use is subject to license terms.
260Sstevel@tonic-gate  */
270Sstevel@tonic-gate 
28*549Smuffin /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
29*549Smuffin /*	  All Rights Reserved  	*/
30*549Smuffin 
310Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
320Sstevel@tonic-gate 
330Sstevel@tonic-gate /*
340Sstevel@tonic-gate  * users.c
350Sstevel@tonic-gate  *
360Sstevel@tonic-gate  *	This file contains the source for the administrative command
370Sstevel@tonic-gate  *	"listusers" (available to the general user population) that
380Sstevel@tonic-gate  *	produces a report containing user login-IDs and their "free
390Sstevel@tonic-gate  *	field" (typically contains the user's name and other information).
400Sstevel@tonic-gate  */
410Sstevel@tonic-gate 
420Sstevel@tonic-gate /*
430Sstevel@tonic-gate  *  Header files referenced:
440Sstevel@tonic-gate  *	sys/types.h	System type definitions
450Sstevel@tonic-gate  *	stdio.h		Definitions for standard I/O functions and constants
460Sstevel@tonic-gate  *	string.h	Definitions for string-handling functions
470Sstevel@tonic-gate  *	grp.h		Definitions for referencing the /etc/group file
480Sstevel@tonic-gate  *	pwd.h		Definitions for referencing the /etc/passwd file
490Sstevel@tonic-gate  *	varargs.h	Definitions for using a variable argument list
500Sstevel@tonic-gate  *	fmtmsg.h	Definitions for using the standard message formatting
510Sstevel@tonic-gate  *			facility
520Sstevel@tonic-gate  */
530Sstevel@tonic-gate 
54*549Smuffin #include <sys/types.h>
55*549Smuffin #include <stdio.h>
56*549Smuffin #include <string.h>
57*549Smuffin #include <grp.h>
58*549Smuffin #include <pwd.h>
59*549Smuffin #include <stdarg.h>
60*549Smuffin #include <fmtmsg.h>
61*549Smuffin #include <stdlib.h>
620Sstevel@tonic-gate 
630Sstevel@tonic-gate 
640Sstevel@tonic-gate /*
650Sstevel@tonic-gate  *  Externals referenced (and not defined by a header file):
660Sstevel@tonic-gate  *	malloc		Allocate memory from main memory
670Sstevel@tonic-gate  *	getopt		Extract the next option from the command line
680Sstevel@tonic-gate  *	optind		The argument count of the next option to extract from
690Sstevel@tonic-gate  *			the command line
700Sstevel@tonic-gate  *	optarg		A pointer to the argument of the option just extracted
710Sstevel@tonic-gate  *			from the command line
720Sstevel@tonic-gate  *	opterr		FLAG:  !0 tells getopt() to write an error message if
730Sstevel@tonic-gate  *			it detects an error
740Sstevel@tonic-gate  *	getpwent	Get next entry from the /etc/passwd file
750Sstevel@tonic-gate  *	getgrent	Get next entry from the /etc/group file
760Sstevel@tonic-gate  *	fmtmsg		Standard message generation facility
770Sstevel@tonic-gate  *	putenv		Modify the environment
780Sstevel@tonic-gate  *	exit		Exit the program
790Sstevel@tonic-gate  */
800Sstevel@tonic-gate 
810Sstevel@tonic-gate /*
820Sstevel@tonic-gate  *  Local constant definitions
830Sstevel@tonic-gate  */
840Sstevel@tonic-gate 
850Sstevel@tonic-gate #ifndef	FALSE
860Sstevel@tonic-gate #define	FALSE			0
870Sstevel@tonic-gate #endif
880Sstevel@tonic-gate 
890Sstevel@tonic-gate #ifndef	TRUE
900Sstevel@tonic-gate #define	TRUE			('t')
910Sstevel@tonic-gate #endif
920Sstevel@tonic-gate 
930Sstevel@tonic-gate #define	USAGE_MSG		"usage: listusers [-g groups] [-l logins]"
940Sstevel@tonic-gate #define	MAXLOGINSIZE		14
950Sstevel@tonic-gate #define	LOGINFIELDSZ		MAXLOGINSIZE+2
960Sstevel@tonic-gate 
970Sstevel@tonic-gate #define	isauserlogin(uid)	(uid >= 100)
98*549Smuffin #define	isasystemlogin(uid)	(uid < 100)
990Sstevel@tonic-gate #define	isausergroup(gid)	(gid >= 100)
1000Sstevel@tonic-gate #define	isasystemgroup(gid)	(gid < 100)
101*549Smuffin 
1020Sstevel@tonic-gate /*
1030Sstevel@tonic-gate  *  Local datatype definitions
1040Sstevel@tonic-gate  */
1050Sstevel@tonic-gate 
1060Sstevel@tonic-gate /*
1070Sstevel@tonic-gate  * This structure describes a specified group name
1080Sstevel@tonic-gate  * (from the -g groups option)
1090Sstevel@tonic-gate  */
1100Sstevel@tonic-gate 
1110Sstevel@tonic-gate struct	reqgrp {
112*549Smuffin 	char		*groupname;
113*549Smuffin 	struct reqgrp	*next;
1140Sstevel@tonic-gate 	int		found;
1150Sstevel@tonic-gate 	gid_t		groupID;
1160Sstevel@tonic-gate };
1170Sstevel@tonic-gate 
1180Sstevel@tonic-gate /*
1190Sstevel@tonic-gate  * This structure describes a specified login name
1200Sstevel@tonic-gate  * (from the -l logins option)
1210Sstevel@tonic-gate  */
1220Sstevel@tonic-gate 
1230Sstevel@tonic-gate struct	reqlogin {
124*549Smuffin 	char		*loginname;
125*549Smuffin 	struct reqlogin	*next;
126*549Smuffin 	int		found;
1270Sstevel@tonic-gate };
128*549Smuffin 
1290Sstevel@tonic-gate /*
1300Sstevel@tonic-gate  *  These functions handle error and warning message writing.
1310Sstevel@tonic-gate  *  (This deals with UNIX(r) standard message generation, so
1320Sstevel@tonic-gate  *  the rest of the code doesn't have to.)
1330Sstevel@tonic-gate  *
1340Sstevel@tonic-gate  *  Functions included:
1350Sstevel@tonic-gate  *	initmsg		Initialize the message handling functions.
1360Sstevel@tonic-gate  *	wrtmsg		Write the message using the standard message
1370Sstevel@tonic-gate  *			generation facility.
1380Sstevel@tonic-gate  *
1390Sstevel@tonic-gate  *  Static data included:
1400Sstevel@tonic-gate  *	fcnlbl		The label for standard messages
1410Sstevel@tonic-gate  *	msgbuf		A buffer to contain the edited message
1420Sstevel@tonic-gate  */
1430Sstevel@tonic-gate 
1440Sstevel@tonic-gate static	char	fcnlbl[MM_MXLABELLN+1];	/* Buffer for message label */
1450Sstevel@tonic-gate static	char	msgbuf[MM_MXTXTLN+1];	/* Buffer for message text */
146*549Smuffin 
1470Sstevel@tonic-gate /*
1480Sstevel@tonic-gate  * void initmsg(p)
1490Sstevel@tonic-gate  *
1500Sstevel@tonic-gate  *	This function initializes the message handling functions.
1510Sstevel@tonic-gate  *
1520Sstevel@tonic-gate  *  Arguments:
1530Sstevel@tonic-gate  *	p	A pointer to a character string that is the name of the
1540Sstevel@tonic-gate  *		command, used to generate the label on messages.  If this
1550Sstevel@tonic-gate  *		string contains a slash ('/'), it only uses the characters
1560Sstevel@tonic-gate  *		beyond the last slash in the string (this permits argv[0]
1570Sstevel@tonic-gate  *		to be used).
1580Sstevel@tonic-gate  *
1590Sstevel@tonic-gate  *  Returns:  Void
1600Sstevel@tonic-gate  */
1610Sstevel@tonic-gate 
1620Sstevel@tonic-gate static void
initmsg(char * p)163*549Smuffin initmsg(char *p)	/* Ptr to command name */
1640Sstevel@tonic-gate {
1650Sstevel@tonic-gate 	/* Automatic data */
1660Sstevel@tonic-gate 	char   *q;		/* Local multi-use pointer */
1670Sstevel@tonic-gate 
1680Sstevel@tonic-gate 	/* Use only the simple filename if there is a slash in the name */
169*549Smuffin 	if ((q = strrchr(p, '/')) == NULL)
170*549Smuffin 		q = p;
171*549Smuffin 	else
172*549Smuffin 		q++;
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate 	/* Build the label for messages */
1750Sstevel@tonic-gate 	(void) snprintf(fcnlbl, sizeof (fcnlbl), "UX:%s", q);
1760Sstevel@tonic-gate 
1770Sstevel@tonic-gate 	/*
1780Sstevel@tonic-gate 	 * Now that we've done all of that work, set things up so that
1790Sstevel@tonic-gate 	 * only the text-component of a message is printed.  (This piece
1800Sstevel@tonic-gate 	 * of code will most probably go away in SVR4.1.
1810Sstevel@tonic-gate 	 */
1820Sstevel@tonic-gate 	(void) putenv("MSGVERB=text");
1830Sstevel@tonic-gate }
184*549Smuffin 
1850Sstevel@tonic-gate /*
1860Sstevel@tonic-gate  *  void wrtmsg(severity, action, tag, text[, txtarg1[, txtarg2[, ...]]])
1870Sstevel@tonic-gate  *
1880Sstevel@tonic-gate  *	This function writes a message using the standard message
1890Sstevel@tonic-gate  * 	generation facility.
1900Sstevel@tonic-gate  *
1910Sstevel@tonic-gate  *  Arguments:
1920Sstevel@tonic-gate  *	severity	The severity-component of the message
1930Sstevel@tonic-gate  *	action		The action-string used to generate the action-
1940Sstevel@tonic-gate  *			component of the message
1950Sstevel@tonic-gate  *	tag		Tag-component of the message
1960Sstevel@tonic-gate  *	text		The text-string used to generate the text-component
1970Sstevel@tonic-gate  *			of the message
1980Sstevel@tonic-gate  *	txtarg		Arguments to be inserted into the "text" string using
1990Sstevel@tonic-gate  *			vsnprintf()
2000Sstevel@tonic-gate  *
2010Sstevel@tonic-gate  *  Returns:  Void
2020Sstevel@tonic-gate  */
2030Sstevel@tonic-gate 
2040Sstevel@tonic-gate /* VARARGS4 */
2050Sstevel@tonic-gate 
2060Sstevel@tonic-gate static void
wrtmsg(int severity,char * action,char * tag,char * text,...)2070Sstevel@tonic-gate wrtmsg(int severity, char *action, char *tag, char *text, ...)
2080Sstevel@tonic-gate {
2090Sstevel@tonic-gate 	/* Automatic data */
2100Sstevel@tonic-gate 	int		errorflg;	/* FLAG:  True if error writing msg */
2110Sstevel@tonic-gate 	va_list		argp;		/* Pointer into vararg list */
2120Sstevel@tonic-gate 
2130Sstevel@tonic-gate 	errorflg = FALSE;
2140Sstevel@tonic-gate 
2150Sstevel@tonic-gate 	/* Generate the error message */
2160Sstevel@tonic-gate 	va_start(argp, text);
217*549Smuffin 	if (text != MM_NULLTXT) {
218*549Smuffin 		/* LINTED */
2190Sstevel@tonic-gate 		errorflg = vsnprintf(msgbuf, sizeof (msgbuf), text, argp) >
220*549Smuffin 		    MM_MXTXTLN;
2210Sstevel@tonic-gate 	}
2220Sstevel@tonic-gate 	(void) fmtmsg(MM_PRINT, fcnlbl, severity,
223*549Smuffin 	    (text == MM_NULLTXT) ? MM_NULLTXT : msgbuf,
224*549Smuffin 	    action, tag);
2250Sstevel@tonic-gate 	va_end(argp);
2260Sstevel@tonic-gate 
2270Sstevel@tonic-gate 	/*
2280Sstevel@tonic-gate 	 *  If there would have been a buffer overflow generating the error
2290Sstevel@tonic-gate 	 *  message, the message will be truncated, so write a message and quit.
2300Sstevel@tonic-gate 	 */
2310Sstevel@tonic-gate 
2320Sstevel@tonic-gate 	if (errorflg) {
233*549Smuffin 		(void) fmtmsg(MM_PRINT, fcnlbl, MM_WARNING,
234*549Smuffin 		    "Internal message buffer overflow",
235*549Smuffin 		    MM_NULLACT, MM_NULLTAG);
236*549Smuffin 		exit(100);
2370Sstevel@tonic-gate 	}
2380Sstevel@tonic-gate }
239*549Smuffin 
2400Sstevel@tonic-gate /*
2410Sstevel@tonic-gate  *  These functions allocate space for the information we gather.
2420Sstevel@tonic-gate  *  It works by having a memory heap with strings allocated from
2430Sstevel@tonic-gate  *  the end of the heap and structures (aligned data) allocated
2440Sstevel@tonic-gate  *  from the beginning of the heap.  It begins with a 4k block of
2450Sstevel@tonic-gate  *  memory then allocates memory in 4k chunks.  These functions
2460Sstevel@tonic-gate  *  should never fail.  If they do, they report the problem and
2470Sstevel@tonic-gate  *  exit with an exit code of 101.
2480Sstevel@tonic-gate  *
2490Sstevel@tonic-gate  *  Functions contained:
2500Sstevel@tonic-gate  *	allocblk	Allocates a block of memory, aligned on a
2510Sstevel@tonic-gate  *			4-byte (double-word) boundary.
2520Sstevel@tonic-gate  *	allocstr	Allocates a block of memory with no particular
2530Sstevel@tonic-gate  *			alignment
2540Sstevel@tonic-gate  *
2550Sstevel@tonic-gate  *  Constant definitions:
2560Sstevel@tonic-gate  *	ALLOCBLKSZ	Size of a chunk of main memory allocated using
2570Sstevel@tonic-gate  *			malloc()
2580Sstevel@tonic-gate  *
2590Sstevel@tonic-gate  *  Static data:
2600Sstevel@tonic-gate  *	nextblkaddr	Address of the next available chunk of aligned
2610Sstevel@tonic-gate  *			space in the heap
2620Sstevel@tonic-gate  *	laststraddr	Address of the last chunk of unaligned space
2630Sstevel@tonic-gate  *			allocated from the heap
2640Sstevel@tonic-gate  *	toomuchspace	Message to write if someone attempts to allocate
2650Sstevel@tonic-gate  *			too much space (>ALLOCBLKSZ bytes)
2660Sstevel@tonic-gate  *	memallocdif	Message to write if there is a problem allocating
2670Sstevel@tonic-gate  *			main memory.
2680Sstevel@tonic-gate  */
2690Sstevel@tonic-gate 
2700Sstevel@tonic-gate #define	ALLOCBLKSZ	4096
2710Sstevel@tonic-gate 
272*549Smuffin static char	*nextblkaddr = NULL;
273*549Smuffin static char	*laststraddr = NULL;
274*549Smuffin static char	*memallocdif =
275*549Smuffin 	"Memory allocation difficulty.  Command terminates";
276*549Smuffin static char	*toomuchspace =
277*549Smuffin 	"Internal space allocation error.  Command terminates";
278*549Smuffin 
2790Sstevel@tonic-gate /*
2800Sstevel@tonic-gate  *  void *allocblk(size)
2810Sstevel@tonic-gate  *	unsigned int	size
2820Sstevel@tonic-gate  *
2830Sstevel@tonic-gate  *	This function allocates a block of aligned (4-byte or double-
2840Sstevel@tonic-gate  *	word boundary) memory from the program's heap.  It returns a
2850Sstevel@tonic-gate  *	pointer to that block of allocated memory.
2860Sstevel@tonic-gate  *
2870Sstevel@tonic-gate  *  Arguments:
2880Sstevel@tonic-gate  *	size		Minimum number of bytes to allocate (will
2890Sstevel@tonic-gate  *			round up to multiple of 4)
2900Sstevel@tonic-gate  *
2910Sstevel@tonic-gate  *  Returns:  void *
2920Sstevel@tonic-gate  *	Pointer to the allocated block of memory
2930Sstevel@tonic-gate  */
2940Sstevel@tonic-gate 
2950Sstevel@tonic-gate static void *
allocblk(unsigned int size)296*549Smuffin allocblk(unsigned int size)
2970Sstevel@tonic-gate {
2980Sstevel@tonic-gate 	/* Automatic data */
2990Sstevel@tonic-gate 	char   *rtnval;
3000Sstevel@tonic-gate 
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate 	/* Make sure the sizes are aligned correctly */
3030Sstevel@tonic-gate 	if ((size = size + (4 - (size % 4))) > ALLOCBLKSZ) {
304*549Smuffin 		wrtmsg(MM_ERROR, MM_NULLACT, MM_NULLTAG, toomuchspace);
305*549Smuffin 		exit(101);
3060Sstevel@tonic-gate 	}
3070Sstevel@tonic-gate 
3080Sstevel@tonic-gate 	/* Set up the value we're going to return */
3090Sstevel@tonic-gate 	rtnval = nextblkaddr;
3100Sstevel@tonic-gate 
3110Sstevel@tonic-gate 	/* Get the space we need off of the heap */
3120Sstevel@tonic-gate 	if ((nextblkaddr += size) >= laststraddr) {
313*549Smuffin 		if ((rtnval = malloc(ALLOCBLKSZ)) == NULL) {
314*549Smuffin 			wrtmsg(MM_ERROR, MM_NULLACT, MM_NULLTAG, memallocdif);
315*549Smuffin 			exit(101);
316*549Smuffin 		}
317*549Smuffin 		laststraddr = rtnval + ALLOCBLKSZ;
318*549Smuffin 		nextblkaddr = rtnval + size;
3190Sstevel@tonic-gate 	}
3200Sstevel@tonic-gate 
3210Sstevel@tonic-gate 	/* We're through */
322*549Smuffin 	return ((void *)rtnval);
3230Sstevel@tonic-gate }
324*549Smuffin 
3250Sstevel@tonic-gate /*
3260Sstevel@tonic-gate  *  char *allocstr(nbytes)
3270Sstevel@tonic-gate  *	unsigned int	nbytes
3280Sstevel@tonic-gate  *
3290Sstevel@tonic-gate  *	This function allocates a block of unaligned memory from the
3300Sstevel@tonic-gate  *	program's heap.  It returns a pointer to that block of allocated
3310Sstevel@tonic-gate  *	memory.
3320Sstevel@tonic-gate  *
3330Sstevel@tonic-gate  *  Arguments:
3340Sstevel@tonic-gate  *	nbytes		Number of bytes to allocate
3350Sstevel@tonic-gate  *
3360Sstevel@tonic-gate  *  Returns:  char *
3370Sstevel@tonic-gate  *	Pointer to the allocated block of memory
3380Sstevel@tonic-gate  */
3390Sstevel@tonic-gate 
3400Sstevel@tonic-gate static char *
allocstr(unsigned int nchars)341*549Smuffin allocstr(unsigned int nchars)
3420Sstevel@tonic-gate {
3430Sstevel@tonic-gate 	if (nchars > ALLOCBLKSZ) {
344*549Smuffin 		wrtmsg(MM_ERROR, MM_NULLACT, MM_NULLTAG, toomuchspace);
345*549Smuffin 		exit(101);
3460Sstevel@tonic-gate 	}
3470Sstevel@tonic-gate 	if ((laststraddr -= nchars) < nextblkaddr) {
348*549Smuffin 		if ((nextblkaddr = malloc(ALLOCBLKSZ)) == NULL) {
349*549Smuffin 			wrtmsg(MM_ERROR, MM_NULLACT, MM_NULLTAG, memallocdif);
350*549Smuffin 			exit(101);
351*549Smuffin 		}
352*549Smuffin 		laststraddr = nextblkaddr + ALLOCBLKSZ - nchars;
3530Sstevel@tonic-gate 	}
354*549Smuffin 	return (laststraddr);
3550Sstevel@tonic-gate }
356*549Smuffin 
3570Sstevel@tonic-gate /*
3580Sstevel@tonic-gate  *  These functions control the group membership list, as found in the
3590Sstevel@tonic-gate  *  /etc/group file.
3600Sstevel@tonic-gate  *
3610Sstevel@tonic-gate  *  Functions included:
3620Sstevel@tonic-gate  *	initmembers		Initialize the membership list (to NULL)
3630Sstevel@tonic-gate  *	addmember		Adds a member to the membership list
3640Sstevel@tonic-gate  *	isamember		Looks for a particular login-ID in the list
3650Sstevel@tonic-gate  *				of members
3660Sstevel@tonic-gate  *
3670Sstevel@tonic-gate  *  Datatype Definitions:
3680Sstevel@tonic-gate  *	struct grpmember	Describes a group member
3690Sstevel@tonic-gate  *
3700Sstevel@tonic-gate  *  Static Data:
3710Sstevel@tonic-gate  *	membershead		Pointer to the head of the list of group members
3720Sstevel@tonic-gate  */
3730Sstevel@tonic-gate 
3740Sstevel@tonic-gate struct	grpmember {
375*549Smuffin 	char			*membername;
376*549Smuffin 	struct grpmember	*next;
3770Sstevel@tonic-gate };
3780Sstevel@tonic-gate 
379*549Smuffin static	struct grpmember	*membershead;
380*549Smuffin 
3810Sstevel@tonic-gate /*
3820Sstevel@tonic-gate  *  void initmembers()
3830Sstevel@tonic-gate  *
3840Sstevel@tonic-gate  *	This function initializes the list of members of specified groups.
3850Sstevel@tonic-gate  *
3860Sstevel@tonic-gate  *  Arguments:  None
3870Sstevel@tonic-gate  *
3880Sstevel@tonic-gate  *  Returns:  Void
3890Sstevel@tonic-gate  */
3900Sstevel@tonic-gate 
3910Sstevel@tonic-gate static void
initmembers(void)392*549Smuffin initmembers(void)
3930Sstevel@tonic-gate {
3940Sstevel@tonic-gate 	/* Set up the members list to be a null member's list */
395*549Smuffin 	membershead = NULL;
3960Sstevel@tonic-gate }
397*549Smuffin 
3980Sstevel@tonic-gate /*
3990Sstevel@tonic-gate  *  void addmember(p)
4000Sstevel@tonic-gate  *	char   *p
4010Sstevel@tonic-gate  *
4020Sstevel@tonic-gate  *	This function adds a member to the group member's list.  The
4030Sstevel@tonic-gate  *	group members list is a list of structures containing a pointer
4040Sstevel@tonic-gate  *	to the member-name and a pointer to the next item in the structure.
4050Sstevel@tonic-gate  *	The structure is not ordered in any particular way.
4060Sstevel@tonic-gate  *
4070Sstevel@tonic-gate  *  Arguments:
4080Sstevel@tonic-gate  *	p	Pointer to the member name
4090Sstevel@tonic-gate  *
4100Sstevel@tonic-gate  *  Returns:  Void
4110Sstevel@tonic-gate  */
4120Sstevel@tonic-gate 
4130Sstevel@tonic-gate static void
addmember(char * p)414*549Smuffin addmember(char *p)
4150Sstevel@tonic-gate {
4160Sstevel@tonic-gate 	/* Automatic data */
417*549Smuffin 	struct grpmember	*new;	/* Member being added */
4180Sstevel@tonic-gate 
419*549Smuffin 	new = (struct grpmember *)allocblk(sizeof (struct grpmember));
420*549Smuffin 	new->membername = strcpy(allocstr((unsigned int)strlen(p)+1), p);
4210Sstevel@tonic-gate 	new->next = membershead;
4220Sstevel@tonic-gate 	membershead = new;
4230Sstevel@tonic-gate }
424*549Smuffin 
4250Sstevel@tonic-gate /*
4260Sstevel@tonic-gate  *  init isamember(p)
4270Sstevel@tonic-gate  *	char   *p
4280Sstevel@tonic-gate  *
4290Sstevel@tonic-gate  *	This function examines the list of group-members for the string
4300Sstevel@tonic-gate  *	referenced by 'p'.  If 'p' is a member of the members list, the
4310Sstevel@tonic-gate  *	function returns TRUE.  Otherwise it returns FALSE.
4320Sstevel@tonic-gate  *
4330Sstevel@tonic-gate  *  Arguments:
4340Sstevel@tonic-gate  *	p	Pointer to the name to search for.
4350Sstevel@tonic-gate  *
4360Sstevel@tonic-gate  *  Returns:  int
4370Sstevel@tonic-gate  *	TRUE	If 'p' is found in the members list,
4380Sstevel@tonic-gate  *	FALSE	otherwise
4390Sstevel@tonic-gate  */
4400Sstevel@tonic-gate 
4410Sstevel@tonic-gate static int
isamember(char * p)442*549Smuffin isamember(char *p)
4430Sstevel@tonic-gate {
4440Sstevel@tonic-gate 	/* Automatic Data */
4450Sstevel@tonic-gate 	int			found;	/* FLAG:  TRUE if login found */
446*549Smuffin 	struct grpmember	*pmem;	/* Pointer to group member */
4470Sstevel@tonic-gate 
4480Sstevel@tonic-gate 
4490Sstevel@tonic-gate 	/* Search the membership list for the 'p' */
4500Sstevel@tonic-gate 	found = FALSE;
451*549Smuffin 	for (pmem = membershead; !found && pmem; pmem = pmem->next) {
452*549Smuffin 		if (strcmp(p, pmem->membername) == 0) found = TRUE;
4530Sstevel@tonic-gate 	}
4540Sstevel@tonic-gate 
4550Sstevel@tonic-gate 	return (found);
4560Sstevel@tonic-gate }
457*549Smuffin 
4580Sstevel@tonic-gate /*
4590Sstevel@tonic-gate  *  These functions handle the display list.  The display list contains
4600Sstevel@tonic-gate  *  all of the information we're to display.  The list contains a pointer
4610Sstevel@tonic-gate  *  to the login-name, a pointer to the free-field (comment), and a pointer
4620Sstevel@tonic-gate  *  to the next item in the list.  The list is ordered alphabetically
4630Sstevel@tonic-gate  *  (ascending) on the login-name field.  The list initially contains a
4640Sstevel@tonic-gate  *  dummy field (to make insertion easier) that contains a login-name of "".
4650Sstevel@tonic-gate  *
4660Sstevel@tonic-gate  *  Functions included:
4670Sstevel@tonic-gate  *	initdisp	Initializes the display list
4680Sstevel@tonic-gate  *	adddisp		Adds information to the display list
4690Sstevel@tonic-gate  *	genreport	Generates a report from the items in the display list
4700Sstevel@tonic-gate  *
4710Sstevel@tonic-gate  *  Datatypes Defined:
4720Sstevel@tonic-gate  *	struct display	Describes the structure that contains the
4730Sstevel@tonic-gate  *			information to be displayed.  Includes pointers
4740Sstevel@tonic-gate  *			to the login-ID, free-field (comment), and the
4750Sstevel@tonic-gate  *			next structure in the list.
4760Sstevel@tonic-gate  *
4770Sstevel@tonic-gate  *  Static Data:
4780Sstevel@tonic-gate  *	displayhead	Pointer to the head of the list of login-IDs to
4790Sstevel@tonic-gate  *			be displayed.  Initially references the null-item
4800Sstevel@tonic-gate  *			on the head of the list.
4810Sstevel@tonic-gate  */
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate struct	display {
484*549Smuffin 	char		*loginID;
485*549Smuffin 	char		*freefield;
486*549Smuffin 	struct display	*next;
4870Sstevel@tonic-gate };
4880Sstevel@tonic-gate 
4890Sstevel@tonic-gate static	struct display *displayhead;
490*549Smuffin 
4910Sstevel@tonic-gate /*
4920Sstevel@tonic-gate  *  void initdisp()
4930Sstevel@tonic-gate  *
4940Sstevel@tonic-gate  *	Initializes the display list.  An empty display list contains a
4950Sstevel@tonic-gate  *	single element, the dummy element.
4960Sstevel@tonic-gate  *
4970Sstevel@tonic-gate  *  Arguments:  None
4980Sstevel@tonic-gate  *
4990Sstevel@tonic-gate  *  Returns:  Void
5000Sstevel@tonic-gate  */
5010Sstevel@tonic-gate 
5020Sstevel@tonic-gate static void
initdisp(void)503*549Smuffin initdisp(void)
5040Sstevel@tonic-gate {
505*549Smuffin 	displayhead = (struct display *)allocblk(sizeof (struct display));
506*549Smuffin 	displayhead->next = NULL;
5070Sstevel@tonic-gate 	displayhead->loginID = "";
5080Sstevel@tonic-gate 	displayhead->freefield = "";
5090Sstevel@tonic-gate }
510*549Smuffin 
5110Sstevel@tonic-gate /*
5120Sstevel@tonic-gate  *  void adddisp(pwent)
5130Sstevel@tonic-gate  *	struct passwd  *pwent
5140Sstevel@tonic-gate  *
5150Sstevel@tonic-gate  *	This function adds the appropriate information from the login
5160Sstevel@tonic-gate  *	description referenced by 'pwent' to the list if information
5170Sstevel@tonic-gate  *	to be displayed.  It only adds the information if the login-ID
5180Sstevel@tonic-gate  *	(user-name) is unique.  It inserts the information in the list
5190Sstevel@tonic-gate  *	in such a way that the list remains ordered alphabetically
5200Sstevel@tonic-gate  *	(ascending) according to the login-ID (user-name).
5210Sstevel@tonic-gate  *
5220Sstevel@tonic-gate  *  Arguments:
5230Sstevel@tonic-gate  *	pwent		Points to the (struct passwd) structure that
5240Sstevel@tonic-gate  *			contains all of the login information on the
5250Sstevel@tonic-gate  *			login being added to the list.  The only
5260Sstevel@tonic-gate  *			information that this function uses is the
5270Sstevel@tonic-gate  *			login-ID (user-name) and the free-field
5280Sstevel@tonic-gate  *			(comment field).
5290Sstevel@tonic-gate  *
5300Sstevel@tonic-gate  *  Returns:  Void
5310Sstevel@tonic-gate  */
5320Sstevel@tonic-gate 
5330Sstevel@tonic-gate static void
adddisp(struct passwd * pwent)534*549Smuffin adddisp(struct passwd *pwent)
5350Sstevel@tonic-gate {
5360Sstevel@tonic-gate 	/* Automatic data */
537*549Smuffin 	struct display	*new;		/* Display item being added */
538*549Smuffin 	struct display	*prev;		/* Previous display item */
539*549Smuffin 	struct display	*current;	/* Next display item */
540*549Smuffin 	int	found;			/* FLAG, insertion point found */
541*549Smuffin 	int	compare = 1;		/* strcmp() compare value */
5420Sstevel@tonic-gate 
5430Sstevel@tonic-gate 
5440Sstevel@tonic-gate 	/* Find where this value belongs in the list */
5450Sstevel@tonic-gate 	prev = displayhead;
5460Sstevel@tonic-gate 	current = displayhead->next;
5470Sstevel@tonic-gate 	found = FALSE;
5480Sstevel@tonic-gate 	while (!found && current) {
549*549Smuffin 		if ((compare = strcmp(current->loginID, pwent->pw_name)) >= 0)
550*549Smuffin 			found = TRUE;
551*549Smuffin 		else {
552*549Smuffin 			prev = current;
553*549Smuffin 			current = current->next;
554*549Smuffin 		}
5550Sstevel@tonic-gate 	}
5560Sstevel@tonic-gate 
5570Sstevel@tonic-gate 	/* Insert this value in the list, only if it is unique though */
5580Sstevel@tonic-gate 	if (compare != 0) {
559*549Smuffin 		/*
560*549Smuffin 		 * Build a display structure containing the value to add to
561*549Smuffin 		 * the list, and add to the list
562*549Smuffin 		 */
563*549Smuffin 		new = (struct display *)allocblk(sizeof (struct display));
564*549Smuffin 		new->loginID =
565*549Smuffin 		    strcpy(allocstr((unsigned int)strlen(pwent->pw_name)+1),
566*549Smuffin 		    pwent->pw_name);
567*549Smuffin 		if (pwent->pw_comment && pwent->pw_comment[0] != '\0')
568*549Smuffin 			new->freefield =
569*549Smuffin 			    strcpy(allocstr(
570*549Smuffin 			    (unsigned int)strlen(pwent->pw_comment)+1),
571*549Smuffin 			    pwent->pw_comment);
572*549Smuffin 		else
573*549Smuffin 			new->freefield =
574*549Smuffin 			    strcpy(allocstr(
575*549Smuffin 			    (unsigned int)strlen(pwent->pw_gecos)+1),
576*549Smuffin 			    pwent->pw_gecos);
577*549Smuffin 		new->next = current;
578*549Smuffin 		prev->next = new;
5790Sstevel@tonic-gate 	}
5800Sstevel@tonic-gate }
581*549Smuffin 
5820Sstevel@tonic-gate /*
5830Sstevel@tonic-gate  *  void genreport()
5840Sstevel@tonic-gate  *
5850Sstevel@tonic-gate  *	This function generates a report on the standard output stream
5860Sstevel@tonic-gate  *	(stdout) containing the login-IDs and the free-fields of the
5870Sstevel@tonic-gate  *	logins that match the list criteria (-g and -l options)
5880Sstevel@tonic-gate  *
5890Sstevel@tonic-gate  *  Arguments:  None
5900Sstevel@tonic-gate  *
5910Sstevel@tonic-gate  *  Returns:  Void
5920Sstevel@tonic-gate  */
5930Sstevel@tonic-gate 
5940Sstevel@tonic-gate static void
genreport(void)595*549Smuffin genreport(void)
5960Sstevel@tonic-gate {
5970Sstevel@tonic-gate 
5980Sstevel@tonic-gate 	/* Automatic data */
599*549Smuffin 	struct display		*current;	/* Value to display */
6000Sstevel@tonic-gate 	int			i;		/* Counter of characters */
6010Sstevel@tonic-gate 
6020Sstevel@tonic-gate 	/*
6030Sstevel@tonic-gate 	 *  Initialization for loop.
6040Sstevel@tonic-gate 	 *  (NOTE:  The first element in the list of logins to display
6050Sstevel@tonic-gate 	 *  is a dummy element.)
6060Sstevel@tonic-gate 	 */
6070Sstevel@tonic-gate 	current = displayhead;
6080Sstevel@tonic-gate 
6090Sstevel@tonic-gate 	/*
6100Sstevel@tonic-gate 	 *  Display elements in the list
6110Sstevel@tonic-gate 	 */
612*549Smuffin 	for (current = displayhead->next; current; current = current->next) {
613*549Smuffin 		(void) fputs(current->loginID, stdout);
614*549Smuffin 		for (i = LOGINFIELDSZ - strlen(current->loginID); --i >= 0;
615*549Smuffin 		    (void) putc(' ', stdout))
616*549Smuffin 			;
617*549Smuffin 		(void) fputs(current->freefield, stdout);
618*549Smuffin 		(void) putc('\n', stdout);
6190Sstevel@tonic-gate 	}
6200Sstevel@tonic-gate }
621*549Smuffin 
6220Sstevel@tonic-gate /*
6230Sstevel@tonic-gate  * listusers [-l logins] [-g groups]
6240Sstevel@tonic-gate  *
6250Sstevel@tonic-gate  *	This command generates a list of user login-IDs.  Specific login-IDs
6260Sstevel@tonic-gate  *	can be listed, as can logins belonging in specific groups.
6270Sstevel@tonic-gate  *
6280Sstevel@tonic-gate  *	-l logins	specifies the login-IDs to display.  "logins" is a
6290Sstevel@tonic-gate  *			comma-list of login-IDs.
6300Sstevel@tonic-gate  *	-g groups	specifies the names of the groups to which a login-ID
6310Sstevel@tonic-gate  *			must belong before it is included in the generated list.
6320Sstevel@tonic-gate  *			"groups" is a comma-list of group names.
6330Sstevel@tonic-gate  * Exit Codes:
6340Sstevel@tonic-gate  *	0	All's well that ends well
6350Sstevel@tonic-gate  *	1	Usage error
6360Sstevel@tonic-gate  */
6370Sstevel@tonic-gate 
638*549Smuffin int
main(int argc,char ** argv)639*549Smuffin main(int argc, char **argv)
6400Sstevel@tonic-gate {
6410Sstevel@tonic-gate 
6420Sstevel@tonic-gate 	/* Automatic data */
6430Sstevel@tonic-gate 
644*549Smuffin 	struct reqgrp	*reqgrphead;	/* Head of the req'd group list */
645*549Smuffin 	struct reqgrp	*pgrp;	/* Current item in the req'd group list */
646*549Smuffin 	struct reqgrp	*qgrp;		/* Prev item in the req'd group list */
647*549Smuffin 	struct reqgrp	*rgrp;	/* Running ptr for scanning group list */
648*549Smuffin 	struct reqlogin	*reqloginhead;	/* Head of req'd login list */
649*549Smuffin 	struct reqlogin	*plogin; /* Current item in the req'd login list */
650*549Smuffin 	struct reqlogin	*qlogin; /* Previous item in the req'd login list */
651*549Smuffin 	struct reqlogin	*rlogin; /* Running ptr for scanning login list */
652*549Smuffin 	struct passwd	*pwent;		/* Ptr to an /etc/passwd entry */
653*549Smuffin 	struct group	*grent;		/* Ptr to an /etc/group entry */
654*549Smuffin 	char	*token;		/* Ptr to a token extracted by strtok() */
655*549Smuffin 	char	**pp;		/* Ptr to a member of a group */
656*549Smuffin 	char	*g_arg;		/* Ptr to the -g option's argument */
657*549Smuffin 	char	*l_arg;		/* Ptr to the -l option's argument */
658*549Smuffin 	int	g_seen;		/* FLAG, true if -g on cmd */
659*549Smuffin 	int	l_seen;		/* FLAG, TRUE if -l is on the command line */
660*549Smuffin 	int	errflg;	/* FLAG, TRUE if there is a command-line problem */
661*549Smuffin 	int	done;		/* FLAG, TRUE if the process (?) is complete */
662*549Smuffin 	int	groupcount;	/* Number of groups specified by the user */
663*549Smuffin 	int	rc;		/* Return code from strcmp() */
664*549Smuffin 	int	c;		/* Character returned from getopt() */
6650Sstevel@tonic-gate 
6660Sstevel@tonic-gate 
6670Sstevel@tonic-gate 	/* Initializations */
6680Sstevel@tonic-gate 	initmsg(argv[0]);
6690Sstevel@tonic-gate 
6700Sstevel@tonic-gate 	/* Command-line processing */
6710Sstevel@tonic-gate 	g_seen = FALSE;
6720Sstevel@tonic-gate 	l_seen = FALSE;
6730Sstevel@tonic-gate 	errflg = FALSE;
6740Sstevel@tonic-gate 	opterr = 0;
6750Sstevel@tonic-gate 	while (!errflg && ((c = getopt(argc, argv, "g:l:")) != EOF)) {
6760Sstevel@tonic-gate 
677*549Smuffin 		/* Case on the option character */
678*549Smuffin 		switch (c) {
6790Sstevel@tonic-gate 
680*549Smuffin 		case 'g':
681*549Smuffin 			if (g_seen)
682*549Smuffin 				errflg = TRUE;
683*549Smuffin 			else {
684*549Smuffin 				g_seen = TRUE;
685*549Smuffin 				g_arg = optarg;
686*549Smuffin 			}
687*549Smuffin 			break;
6880Sstevel@tonic-gate 
689*549Smuffin 		case 'l':
690*549Smuffin 			if (l_seen)
691*549Smuffin 				errflg = TRUE;
692*549Smuffin 			else {
693*549Smuffin 				l_seen = TRUE;
694*549Smuffin 				l_arg = optarg;
695*549Smuffin 			}
696*549Smuffin 			break;
697*549Smuffin 
698*549Smuffin 		default:
699*549Smuffin 			errflg = TRUE;
7000Sstevel@tonic-gate 		}
7010Sstevel@tonic-gate 	}
7020Sstevel@tonic-gate 
7030Sstevel@tonic-gate 	/* Write out a usage message if necessary and quit */
7040Sstevel@tonic-gate 	if (errflg || (optind != argc)) {
705*549Smuffin 		wrtmsg(MM_ERROR, MM_NULLACT, MM_NULLTAG, USAGE_MSG);
706*549Smuffin 		exit(1);
7070Sstevel@tonic-gate 	}
7080Sstevel@tonic-gate 
7090Sstevel@tonic-gate 
7100Sstevel@tonic-gate 	/*
7110Sstevel@tonic-gate 	 *  If the -g groups option was on the command line, build a
7120Sstevel@tonic-gate 	 *  list containing groups we're to list logins for.
7130Sstevel@tonic-gate 	 */
7140Sstevel@tonic-gate 	if (g_seen) {
7150Sstevel@tonic-gate 
716*549Smuffin 		/* Begin with an empty list */
717*549Smuffin 		groupcount = 0;
718*549Smuffin 		reqgrphead = NULL;
7190Sstevel@tonic-gate 
720*549Smuffin 		/* Extract the first token putting an element on the list */
721*549Smuffin 		if ((token = strtok(g_arg, ",")) != NULL) {
722*549Smuffin 			pgrp = (struct reqgrp *)
723*549Smuffin 			    allocblk(sizeof (struct reqgrp));
7240Sstevel@tonic-gate 			pgrp->groupname = token;
7250Sstevel@tonic-gate 			pgrp->found = FALSE;
726*549Smuffin 			pgrp->next = NULL;
7270Sstevel@tonic-gate 			groupcount++;
728*549Smuffin 			reqgrphead = pgrp;
7290Sstevel@tonic-gate 			qgrp = pgrp;
730*549Smuffin 
731*549Smuffin 			/*
732*549Smuffin 			 * Extract subsequent tokens (group names), avoiding
733*549Smuffin 			 * duplicate names (note, list is NOT empty)
734*549Smuffin 			 */
735*549Smuffin 			while (token = strtok(NULL, ",")) {
736*549Smuffin 
737*549Smuffin 				/* Check for duplication */
738*549Smuffin 				rgrp = reqgrphead;
739*549Smuffin 				while (rgrp &&
740*549Smuffin 				    (rc = strcmp(token, rgrp->groupname)))
741*549Smuffin 					rgrp = rgrp->next;
742*549Smuffin 				if (rc != 0) {
743*549Smuffin 
744*549Smuffin 					/* Not a duplicate.  Add on the list */
745*549Smuffin 					pgrp = (struct reqgrp *)
746*549Smuffin 					    allocblk(sizeof (struct reqgrp));
747*549Smuffin 					pgrp->groupname = token;
748*549Smuffin 					pgrp->found = FALSE;
749*549Smuffin 					pgrp->next = NULL;
750*549Smuffin 					groupcount++;
751*549Smuffin 					qgrp->next = pgrp;
752*549Smuffin 					qgrp = pgrp;
753*549Smuffin 				}
754*549Smuffin 			}
7550Sstevel@tonic-gate 		}
7560Sstevel@tonic-gate 	}
7570Sstevel@tonic-gate 
7580Sstevel@tonic-gate 	/*
7590Sstevel@tonic-gate 	 *  If -l logins is on the command line, build a list of logins
7600Sstevel@tonic-gate 	 *  we're to generate reports for.
7610Sstevel@tonic-gate 	 */
7620Sstevel@tonic-gate 	if (l_seen) {
7630Sstevel@tonic-gate 
764*549Smuffin 		/* Begin with a null list */
765*549Smuffin 		reqloginhead = NULL;
7660Sstevel@tonic-gate 
767*549Smuffin 		/* Extract the first token from the argument to the -l option */
768*549Smuffin 		if (token = strtok(l_arg, ",")) {
7690Sstevel@tonic-gate 
770*549Smuffin 			/* Put the first element in the list */
771*549Smuffin 			plogin = (struct reqlogin *)
772*549Smuffin 			    allocblk(sizeof (struct reqlogin));
7730Sstevel@tonic-gate 			plogin->loginname = token;
7740Sstevel@tonic-gate 			plogin->found = FALSE;
775*549Smuffin 			plogin->next = NULL;
776*549Smuffin 			reqloginhead = plogin;
7770Sstevel@tonic-gate 			qlogin = plogin;
778*549Smuffin 
779*549Smuffin 			/*
780*549Smuffin 			 * For each subsequent token in the -l argument's
781*549Smuffin 			 * comma list ...
782*549Smuffin 			 */
783*549Smuffin 
784*549Smuffin 			while (token = strtok(NULL, ",")) {
785*549Smuffin 
786*549Smuffin 				/* Check for duplication (list is not empty) */
787*549Smuffin 				rlogin = reqloginhead;
788*549Smuffin 				while (rlogin &&
789*549Smuffin 				    (rc = strcmp(token, rlogin->loginname)))
790*549Smuffin 					rlogin = rlogin->next;
791*549Smuffin 
792*549Smuffin 				/*
793*549Smuffin 				 * If it's not a duplicate,
794*549Smuffin 				 * add it to the list
795*549Smuffin 				 */
796*549Smuffin 				if (rc != 0) {
797*549Smuffin 					plogin = (struct reqlogin *)
798*549Smuffin 					    allocblk(sizeof (struct reqlogin));
799*549Smuffin 					plogin->loginname = token;
800*549Smuffin 					plogin->found = FALSE;
801*549Smuffin 					plogin->next = NULL;
802*549Smuffin 					qlogin->next = plogin;
803*549Smuffin 					qlogin = plogin;
804*549Smuffin 				}
805*549Smuffin 			}
8060Sstevel@tonic-gate 		}
8070Sstevel@tonic-gate 	}
8080Sstevel@tonic-gate 
8090Sstevel@tonic-gate 
8100Sstevel@tonic-gate 	/*
8110Sstevel@tonic-gate 	 *  If the user requested that only logins be listed in that belong
8120Sstevel@tonic-gate 	 *  to certain groups, compile a list of logins that belong in that
8130Sstevel@tonic-gate 	 *  group.  If the user also requested specific logins, that list
8140Sstevel@tonic-gate 	 *  will be limited to those logins.
8150Sstevel@tonic-gate 	 */
8160Sstevel@tonic-gate 
8170Sstevel@tonic-gate 	/* Initialize the login list */
8180Sstevel@tonic-gate 	initmembers();
8190Sstevel@tonic-gate 	if (g_seen) {
8200Sstevel@tonic-gate 
821*549Smuffin 		/* For each group in the /etc/group file ... */
822*549Smuffin 		while (grent = getgrent()) {
8230Sstevel@tonic-gate 
824*549Smuffin 			/* For each group mentioned with the -g option ... */
825*549Smuffin 			for (pgrp = reqgrphead; (groupcount > 0) && pgrp;
826*549Smuffin 			    pgrp = pgrp->next) {
8270Sstevel@tonic-gate 
828*549Smuffin 				if (pgrp->found == FALSE) {
8290Sstevel@tonic-gate 
830*549Smuffin 					/*
831*549Smuffin 					 * If the mentioned group is found in
832*549Smuffin 					 * the /etc/group file ...
833*549Smuffin 					 */
834*549Smuffin 					if (strcmp(grent->gr_name,
835*549Smuffin 					    pgrp->groupname) == 0) {
8360Sstevel@tonic-gate 
837*549Smuffin 						/*
838*549Smuffin 						 * Mark the entry is found,
839*549Smuffin 						 * remembering the group-ID
840*549Smuffin 						 * for later
841*549Smuffin 						 */
842*549Smuffin 						pgrp->found = TRUE;
843*549Smuffin 						groupcount--;
844*549Smuffin 						pgrp->groupID = grent->gr_gid;
845*549Smuffin 						if (isausergroup(pgrp->groupID))
846*549Smuffin 							for (pp = grent->gr_mem;
847*549Smuffin 							    *pp; pp++)
848*549Smuffin 								addmember(*pp);
849*549Smuffin 					}
850*549Smuffin 				}
8510Sstevel@tonic-gate 			}
8520Sstevel@tonic-gate 		}
8530Sstevel@tonic-gate 
854*549Smuffin 		/*
855*549Smuffin 		 * If any groups weren't found, write a message
856*549Smuffin 		 * indicating such, then continue
857*549Smuffin 		 */
858*549Smuffin 		qgrp = NULL;
859*549Smuffin 		for (pgrp = reqgrphead; pgrp; pgrp = pgrp->next) {
860*549Smuffin 			if (!pgrp->found) {
861*549Smuffin 				wrtmsg(MM_WARNING, MM_NULLACT, MM_NULLTAG,
862*549Smuffin 				    "%s was not found", pgrp->groupname);
863*549Smuffin 				if (!qgrp)
864*549Smuffin 					reqgrphead = pgrp->next;
865*549Smuffin 				else
866*549Smuffin 					qgrp->next = pgrp->next;
867*549Smuffin 			} else if (isasystemgroup(pgrp->groupID)) {
868*549Smuffin 				wrtmsg(MM_WARNING, MM_NULLACT, MM_NULLTAG,
869*549Smuffin 				    "%s is not a user group", pgrp->groupname);
870*549Smuffin 				if (!qgrp)
871*549Smuffin 					reqgrphead = pgrp->next;
872*549Smuffin 				else
873*549Smuffin 					qgrp->next = pgrp->next;
874*549Smuffin 			} else
875*549Smuffin 				qgrp = pgrp;
8760Sstevel@tonic-gate 		}
8770Sstevel@tonic-gate 	}
8780Sstevel@tonic-gate 
8790Sstevel@tonic-gate 
8800Sstevel@tonic-gate 	/* Initialize the list of logins to display */
8810Sstevel@tonic-gate 	initdisp();
8820Sstevel@tonic-gate 
8830Sstevel@tonic-gate 
8840Sstevel@tonic-gate 	/*
8850Sstevel@tonic-gate 	 *  Loop through the /etc/passwd file squirelling away the
8860Sstevel@tonic-gate 	 *  information we need for the display.
8870Sstevel@tonic-gate 	 */
8880Sstevel@tonic-gate 	while (pwent = getpwent()) {
8890Sstevel@tonic-gate 
890*549Smuffin 		/*
891*549Smuffin 		 * The login from /etc/passwd hasn't been included in
892*549Smuffin 		 * the display yet
893*549Smuffin 		 */
894*549Smuffin 		done = FALSE;
8950Sstevel@tonic-gate 
8960Sstevel@tonic-gate 
897*549Smuffin 		/*
898*549Smuffin 		 * If the login was explicitly requested, include it in
899*549Smuffin 		 * the display if it is a user login
900*549Smuffin 		 */
9010Sstevel@tonic-gate 
902*549Smuffin 		if (l_seen) {
903*549Smuffin 			for (plogin = reqloginhead; !done && plogin;
904*549Smuffin 			    plogin = plogin->next) {
905*549Smuffin 				if (strcmp(pwent->pw_name,
906*549Smuffin 				    plogin->loginname) == 0) {
907*549Smuffin 					plogin->found = TRUE;
908*549Smuffin 					if (isauserlogin(pwent->pw_uid))
909*549Smuffin 						adddisp(pwent);
910*549Smuffin 					else
911*549Smuffin 						wrtmsg(MM_WARNING, MM_NULLACT,
912*549Smuffin 						    MM_NULLTAG,
913*549Smuffin 						    "%s is not a user login",
914*549Smuffin 						    plogin->loginname);
915*549Smuffin 					done = TRUE;
916*549Smuffin 				}
917*549Smuffin 			}
9180Sstevel@tonic-gate 		}
9190Sstevel@tonic-gate 
9200Sstevel@tonic-gate 
9210Sstevel@tonic-gate 		/*
922*549Smuffin 		 *  If the login-ID isn't already on the list, if its primary
923*549Smuffin 		 *  group-ID is one of those groups requested, or it is a member
924*549Smuffin 		 *  of the groups requested, include it in the display if it is
925*549Smuffin 		 *  a user login (uid >= 100).
9260Sstevel@tonic-gate 		 */
9270Sstevel@tonic-gate 
928*549Smuffin 		if (isauserlogin(pwent->pw_uid)) {
929*549Smuffin 
930*549Smuffin 			if (!done && g_seen) {
931*549Smuffin 				for (pgrp = reqgrphead; !done && pgrp;
932*549Smuffin 				    pgrp = pgrp->next)
933*549Smuffin 					if (pwent->pw_gid == pgrp->groupID) {
934*549Smuffin 						adddisp(pwent);
935*549Smuffin 						done = TRUE;
936*549Smuffin 					}
937*549Smuffin 				if (!done && isamember(pwent->pw_name)) {
938*549Smuffin 					adddisp(pwent);
939*549Smuffin 					done = TRUE;
940*549Smuffin 				}
941*549Smuffin 			}
942*549Smuffin 
943*549Smuffin 
944*549Smuffin 			/*
945*549Smuffin 			 * If neither -l nor -g is on the command-line and
946*549Smuffin 			 * the login-ID is a user login, include it in
947*549Smuffin 			 * the display.
948*549Smuffin 			 */
949*549Smuffin 
950*549Smuffin 			if (!l_seen && !g_seen)
951*549Smuffin 				adddisp(pwent);
952*549Smuffin 		}
9530Sstevel@tonic-gate 	}
9540Sstevel@tonic-gate 
9550Sstevel@tonic-gate 	/* Let the user know about logins they requested that don't exist */
956*549Smuffin 	if (l_seen)
957*549Smuffin 		for (plogin = reqloginhead; plogin; plogin = plogin->next)
958*549Smuffin 			if (!plogin->found)
959*549Smuffin 				wrtmsg(MM_WARNING, MM_NULLACT, MM_NULLTAG,
960*549Smuffin 				    "%s was not found", plogin->loginname);
9610Sstevel@tonic-gate 
9620Sstevel@tonic-gate 
9630Sstevel@tonic-gate 	/*
9640Sstevel@tonic-gate 	 * Generate a report from this display items we've squirreled away
9650Sstevel@tonic-gate 	 */
9660Sstevel@tonic-gate 	genreport();
9670Sstevel@tonic-gate 
9680Sstevel@tonic-gate 	/*
9690Sstevel@tonic-gate 	 *  We're through!
9700Sstevel@tonic-gate 	 */
971*549Smuffin 	return (0);
9720Sstevel@tonic-gate }
973