xref: /titanic_54/usr/src/cmd/users/users.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23*7c478bd9Sstevel@tonic-gate /*	  All Rights Reserved  	*/
24*7c478bd9Sstevel@tonic-gate 
25*7c478bd9Sstevel@tonic-gate 
26*7c478bd9Sstevel@tonic-gate /*
27*7c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
28*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
29*7c478bd9Sstevel@tonic-gate  */
30*7c478bd9Sstevel@tonic-gate 
31*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
32*7c478bd9Sstevel@tonic-gate 
33*7c478bd9Sstevel@tonic-gate /*
34*7c478bd9Sstevel@tonic-gate  * users.c
35*7c478bd9Sstevel@tonic-gate  *
36*7c478bd9Sstevel@tonic-gate  *	This file contains the source for the administrative command
37*7c478bd9Sstevel@tonic-gate  *	"listusers" (available to the general user population) that
38*7c478bd9Sstevel@tonic-gate  *	produces a report containing user login-IDs and their "free
39*7c478bd9Sstevel@tonic-gate  *	field" (typically contains the user's name and other information).
40*7c478bd9Sstevel@tonic-gate  */
41*7c478bd9Sstevel@tonic-gate 
42*7c478bd9Sstevel@tonic-gate /*
43*7c478bd9Sstevel@tonic-gate  *  Header files referenced:
44*7c478bd9Sstevel@tonic-gate  *	sys/types.h	System type definitions
45*7c478bd9Sstevel@tonic-gate  *	stdio.h		Definitions for standard I/O functions and constants
46*7c478bd9Sstevel@tonic-gate  *	string.h	Definitions for string-handling functions
47*7c478bd9Sstevel@tonic-gate  *	grp.h		Definitions for referencing the /etc/group file
48*7c478bd9Sstevel@tonic-gate  *	pwd.h		Definitions for referencing the /etc/passwd file
49*7c478bd9Sstevel@tonic-gate  *	varargs.h	Definitions for using a variable argument list
50*7c478bd9Sstevel@tonic-gate  *	fmtmsg.h	Definitions for using the standard message formatting
51*7c478bd9Sstevel@tonic-gate  *			facility
52*7c478bd9Sstevel@tonic-gate  */
53*7c478bd9Sstevel@tonic-gate 
54*7c478bd9Sstevel@tonic-gate #include	<sys/types.h>
55*7c478bd9Sstevel@tonic-gate #include	<stdio.h>
56*7c478bd9Sstevel@tonic-gate #include	<string.h>
57*7c478bd9Sstevel@tonic-gate #include	<string.h>
58*7c478bd9Sstevel@tonic-gate #include	<grp.h>
59*7c478bd9Sstevel@tonic-gate #include	<pwd.h>
60*7c478bd9Sstevel@tonic-gate #include	<stdarg.h>
61*7c478bd9Sstevel@tonic-gate #include	<fmtmsg.h>
62*7c478bd9Sstevel@tonic-gate 
63*7c478bd9Sstevel@tonic-gate 
64*7c478bd9Sstevel@tonic-gate /*
65*7c478bd9Sstevel@tonic-gate  *  Externals referenced (and not defined by a header file):
66*7c478bd9Sstevel@tonic-gate  *	malloc		Allocate memory from main memory
67*7c478bd9Sstevel@tonic-gate  *	getopt		Extract the next option from the command line
68*7c478bd9Sstevel@tonic-gate  *	optind		The argument count of the next option to extract from
69*7c478bd9Sstevel@tonic-gate  *			the command line
70*7c478bd9Sstevel@tonic-gate  *	optarg		A pointer to the argument of the option just extracted
71*7c478bd9Sstevel@tonic-gate  *			from the command line
72*7c478bd9Sstevel@tonic-gate  *	opterr		FLAG:  !0 tells getopt() to write an error message if
73*7c478bd9Sstevel@tonic-gate  *			it detects an error
74*7c478bd9Sstevel@tonic-gate  *	getpwent	Get next entry from the /etc/passwd file
75*7c478bd9Sstevel@tonic-gate  *	getgrent	Get next entry from the /etc/group file
76*7c478bd9Sstevel@tonic-gate  *	fmtmsg		Standard message generation facility
77*7c478bd9Sstevel@tonic-gate  *	putenv		Modify the environment
78*7c478bd9Sstevel@tonic-gate  *	exit		Exit the program
79*7c478bd9Sstevel@tonic-gate  */
80*7c478bd9Sstevel@tonic-gate 
81*7c478bd9Sstevel@tonic-gate extern	void	       *malloc();
82*7c478bd9Sstevel@tonic-gate extern	int		getopt();
83*7c478bd9Sstevel@tonic-gate extern	char	       *optarg;
84*7c478bd9Sstevel@tonic-gate extern	int		optind;
85*7c478bd9Sstevel@tonic-gate extern	int		opterr;
86*7c478bd9Sstevel@tonic-gate extern	struct passwd  *getpwent();
87*7c478bd9Sstevel@tonic-gate extern	struct group   *getgrent();
88*7c478bd9Sstevel@tonic-gate extern	int		fmtmsg();
89*7c478bd9Sstevel@tonic-gate extern	int		putenv();
90*7c478bd9Sstevel@tonic-gate extern	void		exit();
91*7c478bd9Sstevel@tonic-gate 
92*7c478bd9Sstevel@tonic-gate /*
93*7c478bd9Sstevel@tonic-gate  *  Local constant definitions
94*7c478bd9Sstevel@tonic-gate  */
95*7c478bd9Sstevel@tonic-gate 
96*7c478bd9Sstevel@tonic-gate #ifndef	FALSE
97*7c478bd9Sstevel@tonic-gate #define	FALSE			0
98*7c478bd9Sstevel@tonic-gate #endif
99*7c478bd9Sstevel@tonic-gate 
100*7c478bd9Sstevel@tonic-gate #ifndef	TRUE
101*7c478bd9Sstevel@tonic-gate #define	TRUE			('t')
102*7c478bd9Sstevel@tonic-gate #endif
103*7c478bd9Sstevel@tonic-gate 
104*7c478bd9Sstevel@tonic-gate #define	USAGE_MSG		"usage: listusers [-g groups] [-l logins]"
105*7c478bd9Sstevel@tonic-gate #define	MAXLOGINSIZE		14
106*7c478bd9Sstevel@tonic-gate #define	LOGINFIELDSZ		MAXLOGINSIZE+2
107*7c478bd9Sstevel@tonic-gate 
108*7c478bd9Sstevel@tonic-gate #define	isauserlogin(uid)	(uid >= 100)
109*7c478bd9Sstevel@tonic-gate #define isasystemlogin(uid)	(uid < 100)
110*7c478bd9Sstevel@tonic-gate #define	isausergroup(gid)	(gid >= 100)
111*7c478bd9Sstevel@tonic-gate #define	isasystemgroup(gid)	(gid < 100)
112*7c478bd9Sstevel@tonic-gate 
113*7c478bd9Sstevel@tonic-gate /*
114*7c478bd9Sstevel@tonic-gate  *  Local datatype definitions
115*7c478bd9Sstevel@tonic-gate  */
116*7c478bd9Sstevel@tonic-gate 
117*7c478bd9Sstevel@tonic-gate /*
118*7c478bd9Sstevel@tonic-gate  * This structure describes a specified group name
119*7c478bd9Sstevel@tonic-gate  * (from the -g groups option)
120*7c478bd9Sstevel@tonic-gate  */
121*7c478bd9Sstevel@tonic-gate 
122*7c478bd9Sstevel@tonic-gate struct	reqgrp {
123*7c478bd9Sstevel@tonic-gate 	char	       *groupname;
124*7c478bd9Sstevel@tonic-gate 	struct reqgrp  *next;
125*7c478bd9Sstevel@tonic-gate 	int		found;
126*7c478bd9Sstevel@tonic-gate 	gid_t		groupID;
127*7c478bd9Sstevel@tonic-gate };
128*7c478bd9Sstevel@tonic-gate 
129*7c478bd9Sstevel@tonic-gate /*
130*7c478bd9Sstevel@tonic-gate  * This structure describes a specified login name
131*7c478bd9Sstevel@tonic-gate  * (from the -l logins option)
132*7c478bd9Sstevel@tonic-gate  */
133*7c478bd9Sstevel@tonic-gate 
134*7c478bd9Sstevel@tonic-gate struct	reqlogin {
135*7c478bd9Sstevel@tonic-gate 	char	        *loginname;
136*7c478bd9Sstevel@tonic-gate 	struct reqlogin *next;
137*7c478bd9Sstevel@tonic-gate 	int	 	 found;
138*7c478bd9Sstevel@tonic-gate };
139*7c478bd9Sstevel@tonic-gate 
140*7c478bd9Sstevel@tonic-gate /*
141*7c478bd9Sstevel@tonic-gate  *  These functions handle error and warning message writing.
142*7c478bd9Sstevel@tonic-gate  *  (This deals with UNIX(r) standard message generation, so
143*7c478bd9Sstevel@tonic-gate  *  the rest of the code doesn't have to.)
144*7c478bd9Sstevel@tonic-gate  *
145*7c478bd9Sstevel@tonic-gate  *  Functions included:
146*7c478bd9Sstevel@tonic-gate  *	initmsg		Initialize the message handling functions.
147*7c478bd9Sstevel@tonic-gate  *	wrtmsg		Write the message using the standard message
148*7c478bd9Sstevel@tonic-gate  *			generation facility.
149*7c478bd9Sstevel@tonic-gate  *
150*7c478bd9Sstevel@tonic-gate  *  Static data included:
151*7c478bd9Sstevel@tonic-gate  *	fcnlbl		The label for standard messages
152*7c478bd9Sstevel@tonic-gate  *	msgbuf		A buffer to contain the edited message
153*7c478bd9Sstevel@tonic-gate  */
154*7c478bd9Sstevel@tonic-gate 
155*7c478bd9Sstevel@tonic-gate static	char	fcnlbl[MM_MXLABELLN+1];	/* Buffer for message label */
156*7c478bd9Sstevel@tonic-gate static	char	msgbuf[MM_MXTXTLN+1];	/* Buffer for message text */
157*7c478bd9Sstevel@tonic-gate 
158*7c478bd9Sstevel@tonic-gate /*
159*7c478bd9Sstevel@tonic-gate  * void initmsg(p)
160*7c478bd9Sstevel@tonic-gate  *
161*7c478bd9Sstevel@tonic-gate  *	This function initializes the message handling functions.
162*7c478bd9Sstevel@tonic-gate  *
163*7c478bd9Sstevel@tonic-gate  *  Arguments:
164*7c478bd9Sstevel@tonic-gate  *	p	A pointer to a character string that is the name of the
165*7c478bd9Sstevel@tonic-gate  *		command, used to generate the label on messages.  If this
166*7c478bd9Sstevel@tonic-gate  *		string contains a slash ('/'), it only uses the characters
167*7c478bd9Sstevel@tonic-gate  *		beyond the last slash in the string (this permits argv[0]
168*7c478bd9Sstevel@tonic-gate  *		to be used).
169*7c478bd9Sstevel@tonic-gate  *
170*7c478bd9Sstevel@tonic-gate  *  Returns:  Void
171*7c478bd9Sstevel@tonic-gate  */
172*7c478bd9Sstevel@tonic-gate 
173*7c478bd9Sstevel@tonic-gate static void
174*7c478bd9Sstevel@tonic-gate initmsg(p)
175*7c478bd9Sstevel@tonic-gate 	char   *p;		/* Ptr to command name */
176*7c478bd9Sstevel@tonic-gate {
177*7c478bd9Sstevel@tonic-gate 	/* Automatic data */
178*7c478bd9Sstevel@tonic-gate 	char   *q;		/* Local multi-use pointer */
179*7c478bd9Sstevel@tonic-gate 
180*7c478bd9Sstevel@tonic-gate 	/* Use only the simple filename if there is a slash in the name */
181*7c478bd9Sstevel@tonic-gate 	if ((q = strrchr(p, '/')) == (char *) NULL) q = p;
182*7c478bd9Sstevel@tonic-gate 	else q++;
183*7c478bd9Sstevel@tonic-gate 
184*7c478bd9Sstevel@tonic-gate 	/* Build the label for messages */
185*7c478bd9Sstevel@tonic-gate 	(void) snprintf(fcnlbl, sizeof (fcnlbl), "UX:%s", q);
186*7c478bd9Sstevel@tonic-gate 
187*7c478bd9Sstevel@tonic-gate 	/*
188*7c478bd9Sstevel@tonic-gate 	 * Now that we've done all of that work, set things up so that
189*7c478bd9Sstevel@tonic-gate 	 * only the text-component of a message is printed.  (This piece
190*7c478bd9Sstevel@tonic-gate 	 * of code will most probably go away in SVR4.1.
191*7c478bd9Sstevel@tonic-gate 	 */
192*7c478bd9Sstevel@tonic-gate 	(void) putenv("MSGVERB=text");
193*7c478bd9Sstevel@tonic-gate }
194*7c478bd9Sstevel@tonic-gate 
195*7c478bd9Sstevel@tonic-gate /*
196*7c478bd9Sstevel@tonic-gate  *  void wrtmsg(severity, action, tag, text[, txtarg1[, txtarg2[, ...]]])
197*7c478bd9Sstevel@tonic-gate  *
198*7c478bd9Sstevel@tonic-gate  *	This function writes a message using the standard message
199*7c478bd9Sstevel@tonic-gate  * 	generation facility.
200*7c478bd9Sstevel@tonic-gate  *
201*7c478bd9Sstevel@tonic-gate  *  Arguments:
202*7c478bd9Sstevel@tonic-gate  *	severity	The severity-component of the message
203*7c478bd9Sstevel@tonic-gate  *	action		The action-string used to generate the action-
204*7c478bd9Sstevel@tonic-gate  *			component of the message
205*7c478bd9Sstevel@tonic-gate  *	tag		Tag-component of the message
206*7c478bd9Sstevel@tonic-gate  *	text		The text-string used to generate the text-component
207*7c478bd9Sstevel@tonic-gate  *			of the message
208*7c478bd9Sstevel@tonic-gate  *	txtarg		Arguments to be inserted into the "text" string using
209*7c478bd9Sstevel@tonic-gate  *			vsnprintf()
210*7c478bd9Sstevel@tonic-gate  *
211*7c478bd9Sstevel@tonic-gate  *  Returns:  Void
212*7c478bd9Sstevel@tonic-gate  */
213*7c478bd9Sstevel@tonic-gate 
214*7c478bd9Sstevel@tonic-gate /* VARARGS4 */
215*7c478bd9Sstevel@tonic-gate 
216*7c478bd9Sstevel@tonic-gate static void
217*7c478bd9Sstevel@tonic-gate wrtmsg(int severity, char *action, char *tag, char *text, ...)
218*7c478bd9Sstevel@tonic-gate {
219*7c478bd9Sstevel@tonic-gate 	/* Automatic data */
220*7c478bd9Sstevel@tonic-gate 	int		errorflg;	/* FLAG:  True if error writing msg */
221*7c478bd9Sstevel@tonic-gate 	va_list		argp;		/* Pointer into vararg list */
222*7c478bd9Sstevel@tonic-gate 
223*7c478bd9Sstevel@tonic-gate 	errorflg = FALSE;
224*7c478bd9Sstevel@tonic-gate 
225*7c478bd9Sstevel@tonic-gate 	/* Generate the error message */
226*7c478bd9Sstevel@tonic-gate 	va_start(argp, text);
227*7c478bd9Sstevel@tonic-gate 	if (text != MM_NULLTXT)
228*7c478bd9Sstevel@tonic-gate 	{
229*7c478bd9Sstevel@tonic-gate 		errorflg = vsnprintf(msgbuf, sizeof (msgbuf), text, argp) >
230*7c478bd9Sstevel@tonic-gate 			 MM_MXTXTLN;
231*7c478bd9Sstevel@tonic-gate 	}
232*7c478bd9Sstevel@tonic-gate 	(void) fmtmsg(MM_PRINT, fcnlbl, severity,
233*7c478bd9Sstevel@tonic-gate 		      (text == MM_NULLTXT) ? MM_NULLTXT : msgbuf,
234*7c478bd9Sstevel@tonic-gate 		      action, tag);
235*7c478bd9Sstevel@tonic-gate 	va_end(argp);
236*7c478bd9Sstevel@tonic-gate 
237*7c478bd9Sstevel@tonic-gate 	/*
238*7c478bd9Sstevel@tonic-gate 	 *  If there would have been a buffer overflow generating the error
239*7c478bd9Sstevel@tonic-gate 	 *  message, the message will be truncated, so write a message and quit.
240*7c478bd9Sstevel@tonic-gate 	 */
241*7c478bd9Sstevel@tonic-gate 
242*7c478bd9Sstevel@tonic-gate 	if (errorflg) {
243*7c478bd9Sstevel@tonic-gate 	    (void) fmtmsg(MM_PRINT, fcnlbl, MM_WARNING,
244*7c478bd9Sstevel@tonic-gate 			  "Internal message buffer overflow",
245*7c478bd9Sstevel@tonic-gate 			  MM_NULLACT, MM_NULLTAG);
246*7c478bd9Sstevel@tonic-gate 	    exit(100);
247*7c478bd9Sstevel@tonic-gate 	}
248*7c478bd9Sstevel@tonic-gate }
249*7c478bd9Sstevel@tonic-gate /* ARGSUSED */
250*7c478bd9Sstevel@tonic-gate 
251*7c478bd9Sstevel@tonic-gate /*
252*7c478bd9Sstevel@tonic-gate  *  These functions allocate space for the information we gather.
253*7c478bd9Sstevel@tonic-gate  *  It works by having a memory heap with strings allocated from
254*7c478bd9Sstevel@tonic-gate  *  the end of the heap and structures (aligned data) allocated
255*7c478bd9Sstevel@tonic-gate  *  from the beginning of the heap.  It begins with a 4k block of
256*7c478bd9Sstevel@tonic-gate  *  memory then allocates memory in 4k chunks.  These functions
257*7c478bd9Sstevel@tonic-gate  *  should never fail.  If they do, they report the problem and
258*7c478bd9Sstevel@tonic-gate  *  exit with an exit code of 101.
259*7c478bd9Sstevel@tonic-gate  *
260*7c478bd9Sstevel@tonic-gate  *  Functions contained:
261*7c478bd9Sstevel@tonic-gate  *	allocblk	Allocates a block of memory, aligned on a
262*7c478bd9Sstevel@tonic-gate  *			4-byte (double-word) boundary.
263*7c478bd9Sstevel@tonic-gate  *	allocstr	Allocates a block of memory with no particular
264*7c478bd9Sstevel@tonic-gate  *			alignment
265*7c478bd9Sstevel@tonic-gate  *
266*7c478bd9Sstevel@tonic-gate  *  Constant definitions:
267*7c478bd9Sstevel@tonic-gate  *	ALLOCBLKSZ	Size of a chunk of main memory allocated using
268*7c478bd9Sstevel@tonic-gate  *			malloc()
269*7c478bd9Sstevel@tonic-gate  *
270*7c478bd9Sstevel@tonic-gate  *  Static data:
271*7c478bd9Sstevel@tonic-gate  *	nextblkaddr	Address of the next available chunk of aligned
272*7c478bd9Sstevel@tonic-gate  *			space in the heap
273*7c478bd9Sstevel@tonic-gate  *	laststraddr	Address of the last chunk of unaligned space
274*7c478bd9Sstevel@tonic-gate  *			allocated from the heap
275*7c478bd9Sstevel@tonic-gate  *	toomuchspace	Message to write if someone attempts to allocate
276*7c478bd9Sstevel@tonic-gate  *			too much space (>ALLOCBLKSZ bytes)
277*7c478bd9Sstevel@tonic-gate  *	memallocdif	Message to write if there is a problem allocating
278*7c478bd9Sstevel@tonic-gate  *			main memory.
279*7c478bd9Sstevel@tonic-gate  */
280*7c478bd9Sstevel@tonic-gate 
281*7c478bd9Sstevel@tonic-gate #define	ALLOCBLKSZ	4096
282*7c478bd9Sstevel@tonic-gate 
283*7c478bd9Sstevel@tonic-gate static	char   *nextblkaddr = (char *) NULL;
284*7c478bd9Sstevel@tonic-gate static	char   *laststraddr = (char *) NULL;
285*7c478bd9Sstevel@tonic-gate static  char   *memallocdif = "Memory allocation difficulty.  Command terminates";
286*7c478bd9Sstevel@tonic-gate static	char   *toomuchspace = "Internal space allocation error.  Command terminates";
287*7c478bd9Sstevel@tonic-gate 
288*7c478bd9Sstevel@tonic-gate /*
289*7c478bd9Sstevel@tonic-gate  *  void *allocblk(size)
290*7c478bd9Sstevel@tonic-gate  *	unsigned int	size
291*7c478bd9Sstevel@tonic-gate  *
292*7c478bd9Sstevel@tonic-gate  *	This function allocates a block of aligned (4-byte or double-
293*7c478bd9Sstevel@tonic-gate  *	word boundary) memory from the program's heap.  It returns a
294*7c478bd9Sstevel@tonic-gate  *	pointer to that block of allocated memory.
295*7c478bd9Sstevel@tonic-gate  *
296*7c478bd9Sstevel@tonic-gate  *  Arguments:
297*7c478bd9Sstevel@tonic-gate  *	size		Minimum number of bytes to allocate (will
298*7c478bd9Sstevel@tonic-gate  *			round up to multiple of 4)
299*7c478bd9Sstevel@tonic-gate  *
300*7c478bd9Sstevel@tonic-gate  *  Returns:  void *
301*7c478bd9Sstevel@tonic-gate  *	Pointer to the allocated block of memory
302*7c478bd9Sstevel@tonic-gate  */
303*7c478bd9Sstevel@tonic-gate 
304*7c478bd9Sstevel@tonic-gate static void *
305*7c478bd9Sstevel@tonic-gate allocblk(size)
306*7c478bd9Sstevel@tonic-gate 	unsigned int	size;
307*7c478bd9Sstevel@tonic-gate {
308*7c478bd9Sstevel@tonic-gate 	/* Automatic data */
309*7c478bd9Sstevel@tonic-gate 	char   *rtnval;
310*7c478bd9Sstevel@tonic-gate 
311*7c478bd9Sstevel@tonic-gate 
312*7c478bd9Sstevel@tonic-gate 	/* Make sure the sizes are aligned correctly */
313*7c478bd9Sstevel@tonic-gate 	if ((size = size + (4 - (size % 4))) > ALLOCBLKSZ) {
314*7c478bd9Sstevel@tonic-gate 	    wrtmsg(MM_ERROR, MM_NULLACT, MM_NULLTAG, toomuchspace);
315*7c478bd9Sstevel@tonic-gate 	    exit(101);
316*7c478bd9Sstevel@tonic-gate 	}
317*7c478bd9Sstevel@tonic-gate 
318*7c478bd9Sstevel@tonic-gate 	/* Set up the value we're going to return */
319*7c478bd9Sstevel@tonic-gate 	rtnval = nextblkaddr;
320*7c478bd9Sstevel@tonic-gate 
321*7c478bd9Sstevel@tonic-gate 	/* Get the space we need off of the heap */
322*7c478bd9Sstevel@tonic-gate 	if ((nextblkaddr += size) >= laststraddr) {
323*7c478bd9Sstevel@tonic-gate 	    if ((rtnval = (char *) malloc(ALLOCBLKSZ)) == (char *) NULL) {
324*7c478bd9Sstevel@tonic-gate 		wrtmsg(MM_ERROR, MM_NULLACT, MM_NULLTAG, memallocdif);
325*7c478bd9Sstevel@tonic-gate 		exit(101);
326*7c478bd9Sstevel@tonic-gate 	    }
327*7c478bd9Sstevel@tonic-gate 	    laststraddr = rtnval + ALLOCBLKSZ;
328*7c478bd9Sstevel@tonic-gate 	    nextblkaddr = rtnval + size;
329*7c478bd9Sstevel@tonic-gate 	}
330*7c478bd9Sstevel@tonic-gate 
331*7c478bd9Sstevel@tonic-gate 	/* We're through */
332*7c478bd9Sstevel@tonic-gate 	return((void *) rtnval);
333*7c478bd9Sstevel@tonic-gate }
334*7c478bd9Sstevel@tonic-gate 
335*7c478bd9Sstevel@tonic-gate /*
336*7c478bd9Sstevel@tonic-gate  *  char *allocstr(nbytes)
337*7c478bd9Sstevel@tonic-gate  *	unsigned int	nbytes
338*7c478bd9Sstevel@tonic-gate  *
339*7c478bd9Sstevel@tonic-gate  *	This function allocates a block of unaligned memory from the
340*7c478bd9Sstevel@tonic-gate  *	program's heap.  It returns a pointer to that block of allocated
341*7c478bd9Sstevel@tonic-gate  *	memory.
342*7c478bd9Sstevel@tonic-gate  *
343*7c478bd9Sstevel@tonic-gate  *  Arguments:
344*7c478bd9Sstevel@tonic-gate  *	nbytes		Number of bytes to allocate
345*7c478bd9Sstevel@tonic-gate  *
346*7c478bd9Sstevel@tonic-gate  *  Returns:  char *
347*7c478bd9Sstevel@tonic-gate  *	Pointer to the allocated block of memory
348*7c478bd9Sstevel@tonic-gate  */
349*7c478bd9Sstevel@tonic-gate 
350*7c478bd9Sstevel@tonic-gate static char *
351*7c478bd9Sstevel@tonic-gate allocstr(nchars)
352*7c478bd9Sstevel@tonic-gate 	unsigned int	nchars;
353*7c478bd9Sstevel@tonic-gate {
354*7c478bd9Sstevel@tonic-gate 	if (nchars > ALLOCBLKSZ) {
355*7c478bd9Sstevel@tonic-gate 	    wrtmsg(MM_ERROR, MM_NULLACT, MM_NULLTAG, toomuchspace);
356*7c478bd9Sstevel@tonic-gate 	    exit(101);
357*7c478bd9Sstevel@tonic-gate 	}
358*7c478bd9Sstevel@tonic-gate 	if ((laststraddr -= nchars) < nextblkaddr) {
359*7c478bd9Sstevel@tonic-gate 	    if ((nextblkaddr = (char *) malloc(ALLOCBLKSZ)) == (char *) NULL) {
360*7c478bd9Sstevel@tonic-gate 		wrtmsg(MM_ERROR, MM_NULLACT, MM_NULLTAG, memallocdif);
361*7c478bd9Sstevel@tonic-gate 		exit(101);
362*7c478bd9Sstevel@tonic-gate 	    }
363*7c478bd9Sstevel@tonic-gate 	    laststraddr = nextblkaddr + ALLOCBLKSZ - nchars;
364*7c478bd9Sstevel@tonic-gate 	}
365*7c478bd9Sstevel@tonic-gate 	return(laststraddr);
366*7c478bd9Sstevel@tonic-gate }
367*7c478bd9Sstevel@tonic-gate 
368*7c478bd9Sstevel@tonic-gate /*
369*7c478bd9Sstevel@tonic-gate  *  These functions control the group membership list, as found in the
370*7c478bd9Sstevel@tonic-gate  *  /etc/group file.
371*7c478bd9Sstevel@tonic-gate  *
372*7c478bd9Sstevel@tonic-gate  *  Functions included:
373*7c478bd9Sstevel@tonic-gate  *	initmembers		Initialize the membership list (to NULL)
374*7c478bd9Sstevel@tonic-gate  *	addmember		Adds a member to the membership list
375*7c478bd9Sstevel@tonic-gate  *	isamember		Looks for a particular login-ID in the list
376*7c478bd9Sstevel@tonic-gate  *				of members
377*7c478bd9Sstevel@tonic-gate  *
378*7c478bd9Sstevel@tonic-gate  *  Datatype Definitions:
379*7c478bd9Sstevel@tonic-gate  *	struct grpmember	Describes a group member
380*7c478bd9Sstevel@tonic-gate  *
381*7c478bd9Sstevel@tonic-gate  *  Static Data:
382*7c478bd9Sstevel@tonic-gate  *	membershead		Pointer to the head of the list of group members
383*7c478bd9Sstevel@tonic-gate  */
384*7c478bd9Sstevel@tonic-gate 
385*7c478bd9Sstevel@tonic-gate struct	grpmember {
386*7c478bd9Sstevel@tonic-gate 	char	               *membername;
387*7c478bd9Sstevel@tonic-gate 	struct grpmember       *next;
388*7c478bd9Sstevel@tonic-gate };
389*7c478bd9Sstevel@tonic-gate 
390*7c478bd9Sstevel@tonic-gate static	struct grpmember       *membershead;
391*7c478bd9Sstevel@tonic-gate 
392*7c478bd9Sstevel@tonic-gate /*
393*7c478bd9Sstevel@tonic-gate  *  void initmembers()
394*7c478bd9Sstevel@tonic-gate  *
395*7c478bd9Sstevel@tonic-gate  *	This function initializes the list of members of specified groups.
396*7c478bd9Sstevel@tonic-gate  *
397*7c478bd9Sstevel@tonic-gate  *  Arguments:  None
398*7c478bd9Sstevel@tonic-gate  *
399*7c478bd9Sstevel@tonic-gate  *  Returns:  Void
400*7c478bd9Sstevel@tonic-gate  */
401*7c478bd9Sstevel@tonic-gate 
402*7c478bd9Sstevel@tonic-gate static void
403*7c478bd9Sstevel@tonic-gate initmembers()
404*7c478bd9Sstevel@tonic-gate {
405*7c478bd9Sstevel@tonic-gate 	/* Set up the members list to be a null member's list */
406*7c478bd9Sstevel@tonic-gate 	membershead = (struct grpmember *) NULL;
407*7c478bd9Sstevel@tonic-gate }
408*7c478bd9Sstevel@tonic-gate 
409*7c478bd9Sstevel@tonic-gate /*
410*7c478bd9Sstevel@tonic-gate  *  void addmember(p)
411*7c478bd9Sstevel@tonic-gate  *	char   *p
412*7c478bd9Sstevel@tonic-gate  *
413*7c478bd9Sstevel@tonic-gate  *	This function adds a member to the group member's list.  The
414*7c478bd9Sstevel@tonic-gate  *	group members list is a list of structures containing a pointer
415*7c478bd9Sstevel@tonic-gate  *	to the member-name and a pointer to the next item in the structure.
416*7c478bd9Sstevel@tonic-gate  *	The structure is not ordered in any particular way.
417*7c478bd9Sstevel@tonic-gate  *
418*7c478bd9Sstevel@tonic-gate  *  Arguments:
419*7c478bd9Sstevel@tonic-gate  *	p	Pointer to the member name
420*7c478bd9Sstevel@tonic-gate  *
421*7c478bd9Sstevel@tonic-gate  *  Returns:  Void
422*7c478bd9Sstevel@tonic-gate  */
423*7c478bd9Sstevel@tonic-gate 
424*7c478bd9Sstevel@tonic-gate static void
425*7c478bd9Sstevel@tonic-gate addmember(p)
426*7c478bd9Sstevel@tonic-gate 	char   *p;
427*7c478bd9Sstevel@tonic-gate {
428*7c478bd9Sstevel@tonic-gate 	/* Automatic data */
429*7c478bd9Sstevel@tonic-gate 	struct grpmember       *new;	/* Member being added */
430*7c478bd9Sstevel@tonic-gate 
431*7c478bd9Sstevel@tonic-gate 	new = (struct grpmember *) allocblk(sizeof(struct grpmember));
432*7c478bd9Sstevel@tonic-gate 	new->membername = strcpy(allocstr((unsigned int) strlen(p)+1), p);
433*7c478bd9Sstevel@tonic-gate 	new->next = membershead;
434*7c478bd9Sstevel@tonic-gate 	membershead = new;
435*7c478bd9Sstevel@tonic-gate }
436*7c478bd9Sstevel@tonic-gate 
437*7c478bd9Sstevel@tonic-gate /*
438*7c478bd9Sstevel@tonic-gate  *  init isamember(p)
439*7c478bd9Sstevel@tonic-gate  *	char   *p
440*7c478bd9Sstevel@tonic-gate  *
441*7c478bd9Sstevel@tonic-gate  *	This function examines the list of group-members for the string
442*7c478bd9Sstevel@tonic-gate  *	referenced by 'p'.  If 'p' is a member of the members list, the
443*7c478bd9Sstevel@tonic-gate  *	function returns TRUE.  Otherwise it returns FALSE.
444*7c478bd9Sstevel@tonic-gate  *
445*7c478bd9Sstevel@tonic-gate  *  Arguments:
446*7c478bd9Sstevel@tonic-gate  *	p	Pointer to the name to search for.
447*7c478bd9Sstevel@tonic-gate  *
448*7c478bd9Sstevel@tonic-gate  *  Returns:  int
449*7c478bd9Sstevel@tonic-gate  *	TRUE	If 'p' is found in the members list,
450*7c478bd9Sstevel@tonic-gate  *	FALSE	otherwise
451*7c478bd9Sstevel@tonic-gate  */
452*7c478bd9Sstevel@tonic-gate 
453*7c478bd9Sstevel@tonic-gate static int
454*7c478bd9Sstevel@tonic-gate isamember(p)
455*7c478bd9Sstevel@tonic-gate 	char   *p;
456*7c478bd9Sstevel@tonic-gate {
457*7c478bd9Sstevel@tonic-gate 	/* Automatic Data */
458*7c478bd9Sstevel@tonic-gate 	int			found;	/* FLAG:  TRUE if login found */
459*7c478bd9Sstevel@tonic-gate 	struct grpmember       *pmem;	/* Pointer to group member */
460*7c478bd9Sstevel@tonic-gate 
461*7c478bd9Sstevel@tonic-gate 
462*7c478bd9Sstevel@tonic-gate 	/* Search the membership list for the 'p' */
463*7c478bd9Sstevel@tonic-gate 	found = FALSE;
464*7c478bd9Sstevel@tonic-gate 	for (pmem = membershead ; !found && pmem ; pmem = pmem->next) {
465*7c478bd9Sstevel@tonic-gate 	    if (strcmp(p, pmem->membername) == 0) found = TRUE;
466*7c478bd9Sstevel@tonic-gate 	}
467*7c478bd9Sstevel@tonic-gate 
468*7c478bd9Sstevel@tonic-gate 	return (found);
469*7c478bd9Sstevel@tonic-gate }
470*7c478bd9Sstevel@tonic-gate 
471*7c478bd9Sstevel@tonic-gate /*
472*7c478bd9Sstevel@tonic-gate  *  These functions handle the display list.  The display list contains
473*7c478bd9Sstevel@tonic-gate  *  all of the information we're to display.  The list contains a pointer
474*7c478bd9Sstevel@tonic-gate  *  to the login-name, a pointer to the free-field (comment), and a pointer
475*7c478bd9Sstevel@tonic-gate  *  to the next item in the list.  The list is ordered alphabetically
476*7c478bd9Sstevel@tonic-gate  *  (ascending) on the login-name field.  The list initially contains a
477*7c478bd9Sstevel@tonic-gate  *  dummy field (to make insertion easier) that contains a login-name of "".
478*7c478bd9Sstevel@tonic-gate  *
479*7c478bd9Sstevel@tonic-gate  *  Functions included:
480*7c478bd9Sstevel@tonic-gate  *	initdisp	Initializes the display list
481*7c478bd9Sstevel@tonic-gate  *	adddisp		Adds information to the display list
482*7c478bd9Sstevel@tonic-gate  *	genreport	Generates a report from the items in the display list
483*7c478bd9Sstevel@tonic-gate  *
484*7c478bd9Sstevel@tonic-gate  *  Datatypes Defined:
485*7c478bd9Sstevel@tonic-gate  *	struct display	Describes the structure that contains the
486*7c478bd9Sstevel@tonic-gate  *			information to be displayed.  Includes pointers
487*7c478bd9Sstevel@tonic-gate  *			to the login-ID, free-field (comment), and the
488*7c478bd9Sstevel@tonic-gate  *			next structure in the list.
489*7c478bd9Sstevel@tonic-gate  *
490*7c478bd9Sstevel@tonic-gate  *  Static Data:
491*7c478bd9Sstevel@tonic-gate  *	displayhead	Pointer to the head of the list of login-IDs to
492*7c478bd9Sstevel@tonic-gate  *			be displayed.  Initially references the null-item
493*7c478bd9Sstevel@tonic-gate  *			on the head of the list.
494*7c478bd9Sstevel@tonic-gate  */
495*7c478bd9Sstevel@tonic-gate 
496*7c478bd9Sstevel@tonic-gate struct	display {
497*7c478bd9Sstevel@tonic-gate 	char	       *loginID;
498*7c478bd9Sstevel@tonic-gate 	char	       *freefield;
499*7c478bd9Sstevel@tonic-gate 	struct display *next;
500*7c478bd9Sstevel@tonic-gate };
501*7c478bd9Sstevel@tonic-gate 
502*7c478bd9Sstevel@tonic-gate static	struct display *displayhead;
503*7c478bd9Sstevel@tonic-gate 
504*7c478bd9Sstevel@tonic-gate /*
505*7c478bd9Sstevel@tonic-gate  *  void initdisp()
506*7c478bd9Sstevel@tonic-gate  *
507*7c478bd9Sstevel@tonic-gate  *	Initializes the display list.  An empty display list contains a
508*7c478bd9Sstevel@tonic-gate  *	single element, the dummy element.
509*7c478bd9Sstevel@tonic-gate  *
510*7c478bd9Sstevel@tonic-gate  *  Arguments:  None
511*7c478bd9Sstevel@tonic-gate  *
512*7c478bd9Sstevel@tonic-gate  *  Returns:  Void
513*7c478bd9Sstevel@tonic-gate  */
514*7c478bd9Sstevel@tonic-gate 
515*7c478bd9Sstevel@tonic-gate static void
516*7c478bd9Sstevel@tonic-gate initdisp()
517*7c478bd9Sstevel@tonic-gate {
518*7c478bd9Sstevel@tonic-gate 	displayhead = (struct display *) allocblk(sizeof(struct display));
519*7c478bd9Sstevel@tonic-gate 	displayhead->next = (struct display *) NULL;
520*7c478bd9Sstevel@tonic-gate 	displayhead->loginID = "";
521*7c478bd9Sstevel@tonic-gate 	displayhead->freefield = "";
522*7c478bd9Sstevel@tonic-gate }
523*7c478bd9Sstevel@tonic-gate 
524*7c478bd9Sstevel@tonic-gate /*
525*7c478bd9Sstevel@tonic-gate  *  void adddisp(pwent)
526*7c478bd9Sstevel@tonic-gate  *	struct passwd  *pwent
527*7c478bd9Sstevel@tonic-gate  *
528*7c478bd9Sstevel@tonic-gate  *	This function adds the appropriate information from the login
529*7c478bd9Sstevel@tonic-gate  *	description referenced by 'pwent' to the list if information
530*7c478bd9Sstevel@tonic-gate  *	to be displayed.  It only adds the information if the login-ID
531*7c478bd9Sstevel@tonic-gate  *	(user-name) is unique.  It inserts the information in the list
532*7c478bd9Sstevel@tonic-gate  *	in such a way that the list remains ordered alphabetically
533*7c478bd9Sstevel@tonic-gate  *	(ascending) according to the login-ID (user-name).
534*7c478bd9Sstevel@tonic-gate  *
535*7c478bd9Sstevel@tonic-gate  *  Arguments:
536*7c478bd9Sstevel@tonic-gate  *	pwent		Points to the (struct passwd) structure that
537*7c478bd9Sstevel@tonic-gate  *			contains all of the login information on the
538*7c478bd9Sstevel@tonic-gate  *			login being added to the list.  The only
539*7c478bd9Sstevel@tonic-gate  *			information that this function uses is the
540*7c478bd9Sstevel@tonic-gate  *			login-ID (user-name) and the free-field
541*7c478bd9Sstevel@tonic-gate  *			(comment field).
542*7c478bd9Sstevel@tonic-gate  *
543*7c478bd9Sstevel@tonic-gate  *  Returns:  Void
544*7c478bd9Sstevel@tonic-gate  */
545*7c478bd9Sstevel@tonic-gate 
546*7c478bd9Sstevel@tonic-gate static void
547*7c478bd9Sstevel@tonic-gate adddisp(pwent)
548*7c478bd9Sstevel@tonic-gate 	struct passwd  *pwent;
549*7c478bd9Sstevel@tonic-gate {
550*7c478bd9Sstevel@tonic-gate 	/* Automatic data */
551*7c478bd9Sstevel@tonic-gate 	struct display	       *new;		/* Display item being added */
552*7c478bd9Sstevel@tonic-gate 	struct display	       *prev;		/* Previous display item */
553*7c478bd9Sstevel@tonic-gate 	struct display	       *current;	/* Next display item */
554*7c478bd9Sstevel@tonic-gate 	int			found;		/* FLAG, insertion point found */
555*7c478bd9Sstevel@tonic-gate 	int			compare = 1;	/* strcmp() compare value */
556*7c478bd9Sstevel@tonic-gate 
557*7c478bd9Sstevel@tonic-gate 
558*7c478bd9Sstevel@tonic-gate 	/* Find where this value belongs in the list */
559*7c478bd9Sstevel@tonic-gate 	prev = displayhead;
560*7c478bd9Sstevel@tonic-gate 	current = displayhead->next;
561*7c478bd9Sstevel@tonic-gate 	found = FALSE;
562*7c478bd9Sstevel@tonic-gate 	while (!found && current) {
563*7c478bd9Sstevel@tonic-gate 	    if ((compare = strcmp(current->loginID, pwent->pw_name)) >= 0)
564*7c478bd9Sstevel@tonic-gate 		found = TRUE;
565*7c478bd9Sstevel@tonic-gate 	    else {
566*7c478bd9Sstevel@tonic-gate 		prev = current;
567*7c478bd9Sstevel@tonic-gate 		current = current->next;
568*7c478bd9Sstevel@tonic-gate 	    }
569*7c478bd9Sstevel@tonic-gate 	}
570*7c478bd9Sstevel@tonic-gate 
571*7c478bd9Sstevel@tonic-gate 	/* Insert this value in the list, only if it is unique though */
572*7c478bd9Sstevel@tonic-gate 	if (compare != 0) {
573*7c478bd9Sstevel@tonic-gate 	    /* Build a display structure containing the value to add to the list, and add to the list */
574*7c478bd9Sstevel@tonic-gate 	    new = (struct display *) allocblk(sizeof(struct display));
575*7c478bd9Sstevel@tonic-gate 	    new->loginID = strcpy(allocstr((unsigned int) strlen(pwent->pw_name)+1), pwent->pw_name);
576*7c478bd9Sstevel@tonic-gate 	    if (pwent->pw_comment && pwent->pw_comment[0] != '\0')
577*7c478bd9Sstevel@tonic-gate 		    new->freefield =
578*7c478bd9Sstevel@tonic-gate 			    strcpy(allocstr((unsigned int)
579*7c478bd9Sstevel@tonic-gate 					    strlen(pwent->pw_comment)+1),
580*7c478bd9Sstevel@tonic-gate 				   pwent->pw_comment);
581*7c478bd9Sstevel@tonic-gate 	    else
582*7c478bd9Sstevel@tonic-gate 		    new->freefield =
583*7c478bd9Sstevel@tonic-gate 			    strcpy(allocstr((unsigned int)
584*7c478bd9Sstevel@tonic-gate 					    strlen(pwent->pw_gecos)+1),
585*7c478bd9Sstevel@tonic-gate 				   pwent->pw_gecos);
586*7c478bd9Sstevel@tonic-gate 	    new->next = current;
587*7c478bd9Sstevel@tonic-gate 	    prev->next = new;
588*7c478bd9Sstevel@tonic-gate 	}
589*7c478bd9Sstevel@tonic-gate }
590*7c478bd9Sstevel@tonic-gate 
591*7c478bd9Sstevel@tonic-gate /*
592*7c478bd9Sstevel@tonic-gate  *  void genreport()
593*7c478bd9Sstevel@tonic-gate  *
594*7c478bd9Sstevel@tonic-gate  *	This function generates a report on the standard output stream
595*7c478bd9Sstevel@tonic-gate  *	(stdout) containing the login-IDs and the free-fields of the
596*7c478bd9Sstevel@tonic-gate  *	logins that match the list criteria (-g and -l options)
597*7c478bd9Sstevel@tonic-gate  *
598*7c478bd9Sstevel@tonic-gate  *  Arguments:  None
599*7c478bd9Sstevel@tonic-gate  *
600*7c478bd9Sstevel@tonic-gate  *  Returns:  Void
601*7c478bd9Sstevel@tonic-gate  */
602*7c478bd9Sstevel@tonic-gate 
603*7c478bd9Sstevel@tonic-gate static void
604*7c478bd9Sstevel@tonic-gate genreport()
605*7c478bd9Sstevel@tonic-gate {
606*7c478bd9Sstevel@tonic-gate 
607*7c478bd9Sstevel@tonic-gate 	/* Automatic data */
608*7c478bd9Sstevel@tonic-gate 	struct display	       *current;	/* Value to display */
609*7c478bd9Sstevel@tonic-gate 	int			i;		/* Counter of characters */
610*7c478bd9Sstevel@tonic-gate 
611*7c478bd9Sstevel@tonic-gate 	/*
612*7c478bd9Sstevel@tonic-gate 	 *  Initialization for loop.
613*7c478bd9Sstevel@tonic-gate 	 *  (NOTE:  The first element in the list of logins to display
614*7c478bd9Sstevel@tonic-gate 	 *  is a dummy element.)
615*7c478bd9Sstevel@tonic-gate 	 */
616*7c478bd9Sstevel@tonic-gate 	current = displayhead;
617*7c478bd9Sstevel@tonic-gate 
618*7c478bd9Sstevel@tonic-gate 	/*
619*7c478bd9Sstevel@tonic-gate 	 *  Display elements in the list
620*7c478bd9Sstevel@tonic-gate 	 */
621*7c478bd9Sstevel@tonic-gate 	for (current = displayhead->next ; current ; current = current->next) {
622*7c478bd9Sstevel@tonic-gate 	    (void) fputs(current->loginID, stdout);
623*7c478bd9Sstevel@tonic-gate 	    for (i = LOGINFIELDSZ - strlen(current->loginID) ; --i >= 0 ; (void) putc(' ', stdout)) ;
624*7c478bd9Sstevel@tonic-gate 	    (void) fputs(current->freefield, stdout);
625*7c478bd9Sstevel@tonic-gate 	    (void) putc('\n', stdout);
626*7c478bd9Sstevel@tonic-gate 	}
627*7c478bd9Sstevel@tonic-gate }
628*7c478bd9Sstevel@tonic-gate 
629*7c478bd9Sstevel@tonic-gate /*
630*7c478bd9Sstevel@tonic-gate  * listusers [-l logins] [-g groups]
631*7c478bd9Sstevel@tonic-gate  *
632*7c478bd9Sstevel@tonic-gate  *	This command generates a list of user login-IDs.  Specific login-IDs
633*7c478bd9Sstevel@tonic-gate  *	can be listed, as can logins belonging in specific groups.
634*7c478bd9Sstevel@tonic-gate  *
635*7c478bd9Sstevel@tonic-gate  *	-l logins	specifies the login-IDs to display.  "logins" is a
636*7c478bd9Sstevel@tonic-gate  *			comma-list of login-IDs.
637*7c478bd9Sstevel@tonic-gate  *	-g groups	specifies the names of the groups to which a login-ID
638*7c478bd9Sstevel@tonic-gate  *			must belong before it is included in the generated list.
639*7c478bd9Sstevel@tonic-gate  *			"groups" is a comma-list of group names.
640*7c478bd9Sstevel@tonic-gate  * Exit Codes:
641*7c478bd9Sstevel@tonic-gate  *	0	All's well that ends well
642*7c478bd9Sstevel@tonic-gate  *	1	Usage error
643*7c478bd9Sstevel@tonic-gate  */
644*7c478bd9Sstevel@tonic-gate 
645*7c478bd9Sstevel@tonic-gate main(argc, argv)
646*7c478bd9Sstevel@tonic-gate 	int	argc;
647*7c478bd9Sstevel@tonic-gate 	char   *argv[];
648*7c478bd9Sstevel@tonic-gate {
649*7c478bd9Sstevel@tonic-gate 
650*7c478bd9Sstevel@tonic-gate 	/* Automatic data */
651*7c478bd9Sstevel@tonic-gate 
652*7c478bd9Sstevel@tonic-gate 	struct reqgrp	       *reqgrphead;	/* Head of the req'd group list */
653*7c478bd9Sstevel@tonic-gate 	struct reqgrp	       *pgrp;		/* Current item in the req'd group list */
654*7c478bd9Sstevel@tonic-gate 	struct reqgrp	       *qgrp;		/* Prev item in the req'd group list */
655*7c478bd9Sstevel@tonic-gate 	struct reqgrp	       *rgrp;		/* Running ptr for scanning group list */
656*7c478bd9Sstevel@tonic-gate 	struct reqlogin	       *reqloginhead;	/* Head of req'd login list */
657*7c478bd9Sstevel@tonic-gate 	struct reqlogin	       *plogin;		/* Current item in the req'd login list */
658*7c478bd9Sstevel@tonic-gate 	struct reqlogin	       *qlogin;		/* Previous item in the req'd login list */
659*7c478bd9Sstevel@tonic-gate 	struct reqlogin	       *rlogin;		/* Running ptr for scanning login list */
660*7c478bd9Sstevel@tonic-gate 	struct passwd	       *pwent;		/* Ptr to an /etc/passwd entry */
661*7c478bd9Sstevel@tonic-gate 	struct group	       *grent;		/* Ptr to an /etc/group entry */
662*7c478bd9Sstevel@tonic-gate 	char		       *token;		/* Ptr to a token extracted by strtok() */
663*7c478bd9Sstevel@tonic-gate 	char		      **pp;		/* Ptr to a member of a group */
664*7c478bd9Sstevel@tonic-gate 	char		       *g_arg;		/* Ptr to the -g option's argument */
665*7c478bd9Sstevel@tonic-gate 	char		       *l_arg;		/* Ptr to the -l option's argument */
666*7c478bd9Sstevel@tonic-gate 	int	 	 	g_seen;		/* FLAG, true if -g on cmd */
667*7c478bd9Sstevel@tonic-gate 	int			l_seen;		/* FLAG, TRUE if -l is on the command line */
668*7c478bd9Sstevel@tonic-gate 	int			errflg;		/* FLAG, TRUE if there is a command-line problem */
669*7c478bd9Sstevel@tonic-gate 	int			done;		/* FLAG, TRUE if the process (?) is complete */
670*7c478bd9Sstevel@tonic-gate 	int			groupcount;	/* Number of groups specified by the user */
671*7c478bd9Sstevel@tonic-gate 	int			rc;		/* Return code from strcmp() */
672*7c478bd9Sstevel@tonic-gate 	int			c;		/* Character returned from getopt() */
673*7c478bd9Sstevel@tonic-gate 
674*7c478bd9Sstevel@tonic-gate 
675*7c478bd9Sstevel@tonic-gate 	/* Initializations */
676*7c478bd9Sstevel@tonic-gate 	initmsg(argv[0]);
677*7c478bd9Sstevel@tonic-gate 
678*7c478bd9Sstevel@tonic-gate 	/* Command-line processing */
679*7c478bd9Sstevel@tonic-gate 	g_seen = FALSE;
680*7c478bd9Sstevel@tonic-gate 	l_seen = FALSE;
681*7c478bd9Sstevel@tonic-gate 	errflg = FALSE;
682*7c478bd9Sstevel@tonic-gate 	opterr = 0;
683*7c478bd9Sstevel@tonic-gate 	while (!errflg && ((c = getopt(argc, argv, "g:l:")) != EOF)) {
684*7c478bd9Sstevel@tonic-gate 
685*7c478bd9Sstevel@tonic-gate 	    /* Case on the option character */
686*7c478bd9Sstevel@tonic-gate 	    switch(c) {
687*7c478bd9Sstevel@tonic-gate 
688*7c478bd9Sstevel@tonic-gate 	    case 'g':
689*7c478bd9Sstevel@tonic-gate 		if (g_seen) errflg = TRUE;
690*7c478bd9Sstevel@tonic-gate 		else {
691*7c478bd9Sstevel@tonic-gate 		    g_seen = TRUE;
692*7c478bd9Sstevel@tonic-gate 		    g_arg = optarg;
693*7c478bd9Sstevel@tonic-gate 		}
694*7c478bd9Sstevel@tonic-gate 		break;
695*7c478bd9Sstevel@tonic-gate 
696*7c478bd9Sstevel@tonic-gate 	    case 'l':
697*7c478bd9Sstevel@tonic-gate 		if (l_seen) errflg = TRUE;
698*7c478bd9Sstevel@tonic-gate 		else {
699*7c478bd9Sstevel@tonic-gate 		    l_seen = TRUE;
700*7c478bd9Sstevel@tonic-gate 		    l_arg = optarg;
701*7c478bd9Sstevel@tonic-gate 		}
702*7c478bd9Sstevel@tonic-gate 		break;
703*7c478bd9Sstevel@tonic-gate 
704*7c478bd9Sstevel@tonic-gate 	    default:
705*7c478bd9Sstevel@tonic-gate 		errflg = TRUE;
706*7c478bd9Sstevel@tonic-gate 	    }
707*7c478bd9Sstevel@tonic-gate 	}
708*7c478bd9Sstevel@tonic-gate 
709*7c478bd9Sstevel@tonic-gate 	/* Write out a usage message if necessary and quit */
710*7c478bd9Sstevel@tonic-gate 	if (errflg || (optind != argc)) {
711*7c478bd9Sstevel@tonic-gate 	    wrtmsg(MM_ERROR, MM_NULLACT, MM_NULLTAG, USAGE_MSG);
712*7c478bd9Sstevel@tonic-gate 	    exit(1);
713*7c478bd9Sstevel@tonic-gate 	}
714*7c478bd9Sstevel@tonic-gate 
715*7c478bd9Sstevel@tonic-gate 
716*7c478bd9Sstevel@tonic-gate 	/*
717*7c478bd9Sstevel@tonic-gate 	 *  If the -g groups option was on the command line, build a
718*7c478bd9Sstevel@tonic-gate 	 *  list containing groups we're to list logins for.
719*7c478bd9Sstevel@tonic-gate 	 */
720*7c478bd9Sstevel@tonic-gate 	if (g_seen) {
721*7c478bd9Sstevel@tonic-gate 
722*7c478bd9Sstevel@tonic-gate 	    /* Begin with an empty list */
723*7c478bd9Sstevel@tonic-gate 	    groupcount = 0;
724*7c478bd9Sstevel@tonic-gate 	    reqgrphead = (struct reqgrp *) NULL;
725*7c478bd9Sstevel@tonic-gate 
726*7c478bd9Sstevel@tonic-gate 	    /* Extract the first token putting an element on the list */
727*7c478bd9Sstevel@tonic-gate 	    if ((token = strtok(g_arg, ",")) != (char *) NULL) {
728*7c478bd9Sstevel@tonic-gate 		pgrp = (struct reqgrp *) allocblk(sizeof(struct reqgrp));
729*7c478bd9Sstevel@tonic-gate 		pgrp->groupname = token;
730*7c478bd9Sstevel@tonic-gate 		pgrp->found = FALSE;
731*7c478bd9Sstevel@tonic-gate 		pgrp->next = (struct reqgrp *) NULL;
732*7c478bd9Sstevel@tonic-gate 		groupcount++;
733*7c478bd9Sstevel@tonic-gate 		reqgrphead = pgrp;
734*7c478bd9Sstevel@tonic-gate 		qgrp = pgrp;
735*7c478bd9Sstevel@tonic-gate 
736*7c478bd9Sstevel@tonic-gate 		/*
737*7c478bd9Sstevel@tonic-gate 		 * Extract subsequent tokens (group names), avoiding duplicate
738*7c478bd9Sstevel@tonic-gate 		 * names (note, list is NOT empty)
739*7c478bd9Sstevel@tonic-gate 		 */
740*7c478bd9Sstevel@tonic-gate 		while (token = strtok((char *) NULL, ",")) {
741*7c478bd9Sstevel@tonic-gate 
742*7c478bd9Sstevel@tonic-gate 		    /* Check for duplication */
743*7c478bd9Sstevel@tonic-gate 		    rgrp = reqgrphead;
744*7c478bd9Sstevel@tonic-gate 		    while (rgrp && (rc = strcmp(token, rgrp->groupname))) rgrp = rgrp->next;
745*7c478bd9Sstevel@tonic-gate 		    if (rc != 0) {
746*7c478bd9Sstevel@tonic-gate 
747*7c478bd9Sstevel@tonic-gate 			/* Not a duplicate.  Add on the list */
748*7c478bd9Sstevel@tonic-gate 			pgrp = (struct reqgrp *) allocblk(sizeof(struct reqgrp));
749*7c478bd9Sstevel@tonic-gate 			pgrp->groupname = token;
750*7c478bd9Sstevel@tonic-gate 			pgrp->found = FALSE;
751*7c478bd9Sstevel@tonic-gate 			pgrp->next = (struct reqgrp *) NULL;
752*7c478bd9Sstevel@tonic-gate 			groupcount++;
753*7c478bd9Sstevel@tonic-gate 			qgrp->next = pgrp;
754*7c478bd9Sstevel@tonic-gate 			qgrp = pgrp;
755*7c478bd9Sstevel@tonic-gate 		    }
756*7c478bd9Sstevel@tonic-gate 		}
757*7c478bd9Sstevel@tonic-gate 	    }
758*7c478bd9Sstevel@tonic-gate 	}
759*7c478bd9Sstevel@tonic-gate 
760*7c478bd9Sstevel@tonic-gate 	/*
761*7c478bd9Sstevel@tonic-gate 	 *  If -l logins is on the command line, build a list of logins
762*7c478bd9Sstevel@tonic-gate 	 *  we're to generate reports for.
763*7c478bd9Sstevel@tonic-gate 	 */
764*7c478bd9Sstevel@tonic-gate 	if (l_seen) {
765*7c478bd9Sstevel@tonic-gate 
766*7c478bd9Sstevel@tonic-gate 	    /* Begin with a null list */
767*7c478bd9Sstevel@tonic-gate 	    reqloginhead = (struct reqlogin *) NULL;
768*7c478bd9Sstevel@tonic-gate 
769*7c478bd9Sstevel@tonic-gate 	    /* Extract the first token from the argument to the -l option */
770*7c478bd9Sstevel@tonic-gate 	    if (token = strtok(l_arg, ",")) {
771*7c478bd9Sstevel@tonic-gate 
772*7c478bd9Sstevel@tonic-gate 		/* Put the first element in the list */
773*7c478bd9Sstevel@tonic-gate 		plogin = (struct reqlogin *) allocblk(sizeof(struct reqlogin));
774*7c478bd9Sstevel@tonic-gate 		plogin->loginname = token;
775*7c478bd9Sstevel@tonic-gate 		plogin->found = FALSE;
776*7c478bd9Sstevel@tonic-gate 		plogin->next = (struct reqlogin *) NULL;
777*7c478bd9Sstevel@tonic-gate 		reqloginhead = plogin;
778*7c478bd9Sstevel@tonic-gate 		qlogin = plogin;
779*7c478bd9Sstevel@tonic-gate 
780*7c478bd9Sstevel@tonic-gate 		/*
781*7c478bd9Sstevel@tonic-gate 		 * For each subsequent token in the -l argument's
782*7c478bd9Sstevel@tonic-gate 		 * comma list ...
783*7c478bd9Sstevel@tonic-gate 		 */
784*7c478bd9Sstevel@tonic-gate 
785*7c478bd9Sstevel@tonic-gate 		while (token = strtok((char *) NULL, ",")) {
786*7c478bd9Sstevel@tonic-gate 
787*7c478bd9Sstevel@tonic-gate 		    /* Check for duplication (list is not empty) */
788*7c478bd9Sstevel@tonic-gate 		    rlogin = reqloginhead;
789*7c478bd9Sstevel@tonic-gate 		    while (rlogin && (rc = strcmp(token, rlogin->loginname)))
790*7c478bd9Sstevel@tonic-gate 			rlogin = rlogin->next;
791*7c478bd9Sstevel@tonic-gate 
792*7c478bd9Sstevel@tonic-gate 		    /* If it's not a duplicate, add it to the list */
793*7c478bd9Sstevel@tonic-gate 		    if (rc != 0) {
794*7c478bd9Sstevel@tonic-gate 			plogin = (struct reqlogin *) allocblk(sizeof(struct reqlogin));
795*7c478bd9Sstevel@tonic-gate 			plogin->loginname = token;
796*7c478bd9Sstevel@tonic-gate 			plogin->found = FALSE;
797*7c478bd9Sstevel@tonic-gate 			plogin->next = (struct reqlogin *) NULL;
798*7c478bd9Sstevel@tonic-gate 			qlogin->next = plogin;
799*7c478bd9Sstevel@tonic-gate 			qlogin = plogin;
800*7c478bd9Sstevel@tonic-gate 		    }
801*7c478bd9Sstevel@tonic-gate 		}
802*7c478bd9Sstevel@tonic-gate 	    }
803*7c478bd9Sstevel@tonic-gate 	}
804*7c478bd9Sstevel@tonic-gate 
805*7c478bd9Sstevel@tonic-gate 
806*7c478bd9Sstevel@tonic-gate 	/*
807*7c478bd9Sstevel@tonic-gate 	 *  If the user requested that only logins be listed in that belong
808*7c478bd9Sstevel@tonic-gate 	 *  to certain groups, compile a list of logins that belong in that
809*7c478bd9Sstevel@tonic-gate 	 *  group.  If the user also requested specific logins, that list
810*7c478bd9Sstevel@tonic-gate 	 *  will be limited to those logins.
811*7c478bd9Sstevel@tonic-gate 	 */
812*7c478bd9Sstevel@tonic-gate 
813*7c478bd9Sstevel@tonic-gate 	/* Initialize the login list */
814*7c478bd9Sstevel@tonic-gate 	initmembers();
815*7c478bd9Sstevel@tonic-gate 	if (g_seen) {
816*7c478bd9Sstevel@tonic-gate 
817*7c478bd9Sstevel@tonic-gate 	    /* For each group in the /etc/group file ... */
818*7c478bd9Sstevel@tonic-gate 	    while (grent = getgrent()) {
819*7c478bd9Sstevel@tonic-gate 
820*7c478bd9Sstevel@tonic-gate 		/* For each group mentioned with the -g option ... */
821*7c478bd9Sstevel@tonic-gate 		for (pgrp = reqgrphead ; (groupcount > 0) && pgrp ; pgrp = pgrp->next) {
822*7c478bd9Sstevel@tonic-gate 
823*7c478bd9Sstevel@tonic-gate 		    if (pgrp->found == FALSE) {
824*7c478bd9Sstevel@tonic-gate 
825*7c478bd9Sstevel@tonic-gate 			/*
826*7c478bd9Sstevel@tonic-gate 			 * If the mentioned group is found in the
827*7c478bd9Sstevel@tonic-gate 			 * /etc/group file ...
828*7c478bd9Sstevel@tonic-gate 			 */
829*7c478bd9Sstevel@tonic-gate 			if (strcmp(grent->gr_name, pgrp->groupname) == 0) {
830*7c478bd9Sstevel@tonic-gate 
831*7c478bd9Sstevel@tonic-gate 			    /* Mark the entry is found, remembering the
832*7c478bd9Sstevel@tonic-gate 			     * group-ID for later */
833*7c478bd9Sstevel@tonic-gate 			    pgrp->found = TRUE;
834*7c478bd9Sstevel@tonic-gate 			    groupcount--;
835*7c478bd9Sstevel@tonic-gate 			    pgrp->groupID = grent->gr_gid;
836*7c478bd9Sstevel@tonic-gate 			    if (isausergroup(pgrp->groupID))
837*7c478bd9Sstevel@tonic-gate 				for (pp = grent->gr_mem ; *pp ; pp++) addmember(*pp);
838*7c478bd9Sstevel@tonic-gate 			}
839*7c478bd9Sstevel@tonic-gate 		    }
840*7c478bd9Sstevel@tonic-gate 		}
841*7c478bd9Sstevel@tonic-gate 	    }
842*7c478bd9Sstevel@tonic-gate 
843*7c478bd9Sstevel@tonic-gate 	    /* If any groups weren't found, write a message
844*7c478bd9Sstevel@tonic-gate 	     * indicating such, then continue */
845*7c478bd9Sstevel@tonic-gate 	    qgrp = (struct reqgrp *) NULL;
846*7c478bd9Sstevel@tonic-gate 	    for (pgrp = reqgrphead ; pgrp ; pgrp = pgrp->next) {
847*7c478bd9Sstevel@tonic-gate 		if (!pgrp->found) {
848*7c478bd9Sstevel@tonic-gate 		    wrtmsg(MM_WARNING, MM_NULLACT, MM_NULLTAG, "%s was not found", pgrp->groupname);
849*7c478bd9Sstevel@tonic-gate 		    if (!qgrp) reqgrphead = pgrp->next;
850*7c478bd9Sstevel@tonic-gate 		    else qgrp->next = pgrp->next;
851*7c478bd9Sstevel@tonic-gate 		}
852*7c478bd9Sstevel@tonic-gate 		else if (isasystemgroup(pgrp->groupID)) {
853*7c478bd9Sstevel@tonic-gate 		    wrtmsg(MM_WARNING, MM_NULLACT, MM_NULLTAG, "%s is not a user group", pgrp->groupname);
854*7c478bd9Sstevel@tonic-gate 		    if (!qgrp) reqgrphead = pgrp->next;
855*7c478bd9Sstevel@tonic-gate 		    else qgrp->next = pgrp->next;
856*7c478bd9Sstevel@tonic-gate 		}
857*7c478bd9Sstevel@tonic-gate 		else qgrp = pgrp;
858*7c478bd9Sstevel@tonic-gate 	    }
859*7c478bd9Sstevel@tonic-gate 	}
860*7c478bd9Sstevel@tonic-gate 
861*7c478bd9Sstevel@tonic-gate 
862*7c478bd9Sstevel@tonic-gate 	/* Initialize the list of logins to display */
863*7c478bd9Sstevel@tonic-gate 	initdisp();
864*7c478bd9Sstevel@tonic-gate 
865*7c478bd9Sstevel@tonic-gate 
866*7c478bd9Sstevel@tonic-gate 	/*
867*7c478bd9Sstevel@tonic-gate 	 *  Loop through the /etc/passwd file squirelling away the
868*7c478bd9Sstevel@tonic-gate 	 *  information we need for the display.
869*7c478bd9Sstevel@tonic-gate 	 */
870*7c478bd9Sstevel@tonic-gate 	while (pwent = getpwent()) {
871*7c478bd9Sstevel@tonic-gate 
872*7c478bd9Sstevel@tonic-gate 	    /* The login from /etc/passwd hasn't been included in
873*7c478bd9Sstevel@tonic-gate 	     * the display yet */
874*7c478bd9Sstevel@tonic-gate 	    done = FALSE;
875*7c478bd9Sstevel@tonic-gate 
876*7c478bd9Sstevel@tonic-gate 
877*7c478bd9Sstevel@tonic-gate 	    /*
878*7c478bd9Sstevel@tonic-gate 	     * If the login was explicitly requested, include it in
879*7c478bd9Sstevel@tonic-gate 	     * the display if it is a user login
880*7c478bd9Sstevel@tonic-gate 	     */
881*7c478bd9Sstevel@tonic-gate 
882*7c478bd9Sstevel@tonic-gate 	    if (l_seen) {
883*7c478bd9Sstevel@tonic-gate 		for (plogin = reqloginhead ; !done && plogin ; plogin = plogin->next) {
884*7c478bd9Sstevel@tonic-gate 		    if (strcmp(pwent->pw_name, plogin->loginname) == 0) {
885*7c478bd9Sstevel@tonic-gate 			plogin->found = TRUE;
886*7c478bd9Sstevel@tonic-gate 			if (isauserlogin(pwent->pw_uid)) adddisp(pwent);
887*7c478bd9Sstevel@tonic-gate 			else
888*7c478bd9Sstevel@tonic-gate 			    wrtmsg(MM_WARNING, MM_NULLACT, MM_NULLTAG,
889*7c478bd9Sstevel@tonic-gate 				   "%s is not a user login", plogin->loginname);
890*7c478bd9Sstevel@tonic-gate 			done = TRUE;
891*7c478bd9Sstevel@tonic-gate 		    }
892*7c478bd9Sstevel@tonic-gate 		}
893*7c478bd9Sstevel@tonic-gate 	    }
894*7c478bd9Sstevel@tonic-gate 
895*7c478bd9Sstevel@tonic-gate 
896*7c478bd9Sstevel@tonic-gate 	    /*
897*7c478bd9Sstevel@tonic-gate 	     *  If the login-ID isn't already on the list, if its primary
898*7c478bd9Sstevel@tonic-gate 	     *  group-ID is one of those groups requested, or it is a member
899*7c478bd9Sstevel@tonic-gate 	     *  of the groups requested, include it in the display if it is
900*7c478bd9Sstevel@tonic-gate 	     *  a user login (uid >= 100).
901*7c478bd9Sstevel@tonic-gate 	     */
902*7c478bd9Sstevel@tonic-gate 
903*7c478bd9Sstevel@tonic-gate 	    if (isauserlogin(pwent->pw_uid)) {
904*7c478bd9Sstevel@tonic-gate 
905*7c478bd9Sstevel@tonic-gate 		if (!done && g_seen) {
906*7c478bd9Sstevel@tonic-gate 		    for (pgrp = reqgrphead ; !done && pgrp ; pgrp = pgrp->next)
907*7c478bd9Sstevel@tonic-gate 			if (pwent->pw_gid == pgrp->groupID) {
908*7c478bd9Sstevel@tonic-gate 			    adddisp(pwent);
909*7c478bd9Sstevel@tonic-gate 			    done = TRUE;
910*7c478bd9Sstevel@tonic-gate 		    }
911*7c478bd9Sstevel@tonic-gate 		    if (!done && isamember(pwent->pw_name)) {
912*7c478bd9Sstevel@tonic-gate 			adddisp(pwent);
913*7c478bd9Sstevel@tonic-gate 			done = TRUE;
914*7c478bd9Sstevel@tonic-gate 		    }
915*7c478bd9Sstevel@tonic-gate 		}
916*7c478bd9Sstevel@tonic-gate 
917*7c478bd9Sstevel@tonic-gate 
918*7c478bd9Sstevel@tonic-gate 		/*
919*7c478bd9Sstevel@tonic-gate 		 *  If neither -l nor -g is on the command-line and the login-ID
920*7c478bd9Sstevel@tonic-gate 		 *  is a user login, include it in the display.
921*7c478bd9Sstevel@tonic-gate 		 */
922*7c478bd9Sstevel@tonic-gate 
923*7c478bd9Sstevel@tonic-gate 		if (!l_seen && !g_seen) adddisp(pwent);
924*7c478bd9Sstevel@tonic-gate 	    }
925*7c478bd9Sstevel@tonic-gate 	}
926*7c478bd9Sstevel@tonic-gate 
927*7c478bd9Sstevel@tonic-gate 	/* Let the user know about logins they requested that don't exist */
928*7c478bd9Sstevel@tonic-gate 	if (l_seen) for (plogin = reqloginhead ; plogin ; plogin = plogin->next)
929*7c478bd9Sstevel@tonic-gate 	    if (!plogin->found)
930*7c478bd9Sstevel@tonic-gate 		wrtmsg(MM_WARNING, MM_NULLACT, MM_NULLTAG, "%s was not found", plogin->loginname);
931*7c478bd9Sstevel@tonic-gate 
932*7c478bd9Sstevel@tonic-gate 
933*7c478bd9Sstevel@tonic-gate 	/*
934*7c478bd9Sstevel@tonic-gate 	 * Generate a report from this display items we've squirreled away
935*7c478bd9Sstevel@tonic-gate 	 */
936*7c478bd9Sstevel@tonic-gate 	genreport();
937*7c478bd9Sstevel@tonic-gate 
938*7c478bd9Sstevel@tonic-gate 	/*
939*7c478bd9Sstevel@tonic-gate 	 *  We're through!
940*7c478bd9Sstevel@tonic-gate 	 */
941*7c478bd9Sstevel@tonic-gate 	exit(0);
942*7c478bd9Sstevel@tonic-gate 
943*7c478bd9Sstevel@tonic-gate #ifdef	lint
944*7c478bd9Sstevel@tonic-gate 	return(0);
945*7c478bd9Sstevel@tonic-gate #endif
946*7c478bd9Sstevel@tonic-gate }
947