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