logins.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
#pragma ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.15.1.2 */
/*
* logins.c
*
* This file contains the source for the administrative command
* "logins" (available to the administrator) that produces a report
* containing login-IDs and other requested information.
*/
/*
* Header files referenced:
* sys/types.h System data types
* stdio.h Definitions for standard I/O functions and constants
* unistd.h Standard UNIX definitions
* string.h Definitions for string-handling functions
* ctype.h Character-type definitions
* grp.h Definitions for referencing the /etc/group file
* pwd.h Definitions for referencing the /etc/passwd file
* shadow.h Definitions for the shadow password file /etc/shadow
* time.h Time definitions (ctime(), asctime(), etc.)
* stdarg.h Definitions for using a variable argument list
* fmtmsg.h Definitions for using the standard message generator
*/
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <grp.h>
#include <pwd.h>
#include <shadow.h>
#include <time.h>
#include <stdarg.h>
#include <fmtmsg.h>
#include <locale.h>
/*
* Externals referenced (and not defined by a header file):
* malloc Allocate memory from main memory
* getopt Extract the next option from the command line
* optind The argument count of the next option to extract
* from the command line
* optarg A pointer to the argument of the option just extracted
* from the command line
* opterr FLAG: !0 tells getopt() to write an error message if
* it detects an error
* getpwent Get next entry from the /etc/passwd file
* getpwuid Get next entry from the /etc/passwd file that has a
* specific user-ID
* fgetpwent Get next entry from an /etc/passwd-like file
* setpwent Rewind the /etc/passwd file
* endpwent Quit using the /etc/passwd file
* getgrent Get next entry from the /etc/group file
* setgrent Rewind the /etc/group file
* endgrent Quit using the /etc/passwd file
* getspnam Get the next entry for a specific login-ID from the
* /etc/shadow file
* setspent Rewind the /etc/shadow file
* endspent Quit using the /etc/shadow file
* fmtmsg Interface to the standard message generation facility
* putenv Modify the environment
* exit Exit the program
*/
extern void *malloc();
extern int getopt();
extern char *optarg;
extern int optind;
extern int opterr;
extern struct passwd *getpwent();
extern struct passwd *getpwuid();
extern struct passwd *fgetpwent();
extern void setpwent();
extern void endpwent();
extern struct group *getgrent();
extern void setgrent();
extern void endgrent();
extern struct spwd *getspnam();
extern void setspent();
extern void endspent();
extern int fmtmsg();
extern int putenv();
extern void exit();
/*
* Local constant definitions
* TRUE Boolean constant
* FALSE Boolean constant
* USAGE_MSG Message used to display a usage error
* MAXLOGINSIZE Maximum length of a valid login-ID
* MAXSYSTEMLOGIN Maximum value of a system user-ID.
* OPTSTR Options to this command
* ROOT_ID The user-ID of an administrator
*/
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE ((int) 't')
#endif
#define USAGE_MSG "usage: logins [-admopstux] [-g groups] [-l logins]"
#define MAXLOGINSIZE 14
#define MAXSYSTEMLOGIN 99
#define OPTSTR "adg:l:mopstux"
#define ROOT_ID 0
/*
* The following macros do their function for now but will probably have
* to be replaced by functions sometime in the near future. The maximum
* system login value may someday be administerable, in which case these
* will have to be changed to become functions
*
* isasystemlogin Returns TRUE if the user-ID in the "struct passwd"
* structure referenced by the function's argument is
* less than or equal to the maximum value for a system
* user-ID, FALSE otherwise.
* isauserlogin Returns TRUE if the user-ID in the "struct passwd"
* structure referenced by the function's argument is
* greater than the maximum value for a system user-ID,
* FALSE otherwise.
*/
#define isauserlogin(pw) (pw->pw_uid > MAXSYSTEMLOGIN)
#define isasystemlogin(pw) (pw->pw_uid <= MAXSYSTEMLOGIN)
/*
* Local datatype definitions
* struct reqgrp Describes a group as requested through the
* -g option
* struct reqlogin Describes a login-ID as requested through
* the -l option
* struct pwdinfo Describes a password's aging information,
* as extracted from /etc/shadow
* struct secgrp Describes a login-ID's secondary group
*/
/* Describes a specified group name (from the -g groups option) */
struct reqgrp {
char *groupname; /* Requested group name */
struct reqgrp *next; /* Next item in the list */
int found; /* TRUE if group in /etc/group */
gid_t groupID; /* Group's ID */
};
/* Describes a specified login name (from the -l logins option) */
struct reqlogin {
char *loginname; /* Requested login name */
struct reqlogin *next; /* Next item in the list */
int found; /* TRUE if login in /etc/passwd */
};
/*
* This structure describes a password's information
*/
struct pwdinfo {
long datechg; /* Date the password was changed (mmddyy) */
char *passwdstatus; /* Password status */
long mindaystilchg; /* Min days b4 pwd can change again */
long maxdaystilchg; /* Max days b4 pwd can change again */
long warninterval; /* Days before expire to warn user */
long inactive; /* Lapsed days of inactivity before lock */
long expdate; /* Date of expiration (mmddyy) */
};
/* This structure describes secondary groups that a user belongs to */
struct secgrp {
char *groupname; /* Name of the group */
struct secgrp *next; /* Next item in the list */
gid_t groupID; /* Group-ID */
};
/*
* These functions handle error and warning message writing.
* (This deals with UNIX(r) standard message generation, so
* the rest of the code doesn't have to.)
*
* Functions included:
* initmsg Initialize the message handling functions.
* wrtmsg Write the message using fmtmsg().
*
* Static data included:
* fcnlbl The label for standard messages
* msgbuf A buffer to contain the edited message
*/
static char fcnlbl[MM_MXLABELLN+1]; /* Buffer for message label */
static char msgbuf[MM_MXTXTLN+1]; /* Buffer for message text */
/*
* void initmsg(p)
*
* This function initializes the message handling functions.
*
* Arguments:
* p A pointer to a character string that is the name of the
* function, used to generate the label on messages. If this
* string contains a slash ('/'), it only uses the characters
* beyond the last slash in the string (this permits argv[0]
* to be used).
*
* Returns: Void
*/
static void
initmsg(p)
char *p; /* Command name (as invoked) */
{
/* Automatic data */
char *q; /* Local multi-use pointer */
/* Use only the simple filename if there is a slash in the name */
if (!(q = strrchr(p, '/'))) q = p;
else q++;
/* Build the label for messages */
(void) sprintf(fcnlbl, "UX:%s", q);
/* Restrict messages to the text-component */
(void) putenv("MSGVERB=text");
}
/*
* void wrtmsg(severity, action, tag, text[, txtarg1[, txtarg2[, ...]]])
*
* This function writes a message using fmtmsg()
*
* Arguments:
* severity The severity-component of the message
* action The action-string used to generate the
* action-component of the message
* tag Tag-component of the message
* text The text-string used to generate the text-
* component of the message
* txtarg Arguments to be inserted into the "text"
* string using vsprintf()
*
* Returns: Void
*/
/*VARARGS4*/
static void
wrtmsg(int severity, char *action, char *tag, char *text, ...)
{
/* Automatic data */
int errorflg; /* TRUE if problem generating message */
va_list argp; /* Pointer into vararg list */
/* No problems yet */
errorflg = FALSE;
/* Generate the error message */
va_start(argp, text);
if (text != MM_NULLTXT) errorflg = vsprintf(msgbuf, text, argp) > MM_MXTXTLN;
(void) fmtmsg(MM_PRINT, fcnlbl, severity,
(text == MM_NULLTXT) ? MM_NULLTXT : msgbuf,
action, tag);
va_end(argp);
/*
* If there was a buffer overflow generating the error message,
* write a message and quit (things are probably corrupt in the
* static data space now
*/
if (errorflg) {
(void) fmtmsg(MM_PRINT, fcnlbl, MM_WARNING,
gettext("Internal message buffer overflow"),
MM_NULLACT, MM_NULLTAG);
exit(100);
}
}
/*ARGSUSED*/
/*
* These functions allocate space for the information we gather.
* It works by having a memory heap with strings allocated from
* the end of the heap and structures (aligned data) allocated
* from the beginning of the heap. It begins with a 4k block of
* memory then allocates memory in 4k chunks. These functions
* should never fail. If they do, they report the problem and
* exit with an exit code of 101.
*
* Functions contained:
* allocblk Allocates a block of memory, aligned on a
* 4-byte (double-word) boundary.
* allocstr Allocates a block of memory with no
* particular alignment
*
* Constant definitions:
* ALLOCBLKSZ Size of a chunk of main memory allocated
* using malloc()
*
* Static data:
* nextblkaddr Address of the next available chunk of
* aligned space in the heap
* laststraddr Address of the last chunk of unaligned space
* allocated from the heap
* toomuchspace Message to write if someone attempts to allocate
* too much space (>ALLOCBLKSZ bytes)
* memallocdif Message to write if there is a problem
* allocating main menory.
*/
#define ALLOCBLKSZ 4096
static char *nextblkaddr = (char *) NULL;
static char *laststraddr = (char *) NULL;
#define MEMALLOCDIF "Memory allocation difficulty. Command terminates"
#define TOOMUCHSPACE "Internal space allocation error. Command terminates"
/*
* void *allocblk(size)
* unsigned int size
*
* This function allocates a block of aligned (4-byte or
* double-word boundary) memory from the program's heap.
* It returns a pointer to that block of allocated memory.
*
* Arguments:
* size Minimum number of bytes to allocate (will
* round up to multiple of 4)
*
* Returns: void *
* Pointer to the allocated block of memory
*/
static void *
allocblk(size)
unsigned int size;
{
/* Automatic data */
char *rtnval;
/* Make sure the sizes are aligned correctly */
if ((size = size + (4 - (size % 4))) > ALLOCBLKSZ) {
wrtmsg(MM_ERROR, MM_NULLACT, MM_NULLTAG, gettext(TOOMUCHSPACE));
exit(101);
}
/* Set up the value we're going to return */
rtnval = nextblkaddr;
/* Get the space we need off of the heap */
if ((nextblkaddr += size) >= laststraddr) {
if (!(rtnval = (char *) malloc(ALLOCBLKSZ))) {
wrtmsg(MM_ERROR, MM_NULLACT, MM_NULLTAG, gettext(MEMALLOCDIF));
exit(101);
}
laststraddr = rtnval + ALLOCBLKSZ;
nextblkaddr = rtnval + size;
}
/* We're through */
return((void *) rtnval);
}
/*
* char *allocstr(nbytes)
* unsigned int nbytes
*
* This function allocates a block of unaligned memory from the
* program's heap. It returns a pointer to that block of allocated
* memory.
*
* Arguments:
* nbytes Number of bytes to allocate
*
* Returns: char *
* Pointer to the allocated block of memory
*/
static char *
allocstr(nchars)
unsigned int nchars;
{
if (nchars > ALLOCBLKSZ) {
wrtmsg(MM_ERROR, MM_NULLACT, MM_NULLTAG, gettext(TOOMUCHSPACE));
exit(101);
}
if (laststraddr == NULL ||
(laststraddr -= nchars) < nextblkaddr) {
if (!(nextblkaddr = (char *) malloc(ALLOCBLKSZ))) {
wrtmsg(MM_ERROR, MM_NULLACT, MM_NULLTAG, gettext(MEMALLOCDIF));
exit(101);
}
laststraddr = nextblkaddr + ALLOCBLKSZ - nchars;
}
return(laststraddr);
}
/*
* These functions control the group membership list, as found in
* the /etc/group file.
*
* Functions included:
* initmembers Initialize the membership list (to NULL)
* addmember Adds a member to the membership list
* isamember Looks for a particular login-ID in the
* list of members
*
* Datatype Definitions:
* struct grpmember Describes a group member
*
* Static Data:
* membershead Pointer to the head of the list of
* group members
*/
struct grpmember {
char *membername;
struct grpmember *next;
};
static struct grpmember *membershead;
/*
* void initmembers()
*
* This function initializes the list of members of specified groups.
*
* Arguments: None
*
* Returns: Void
*/
static void
initmembers()
{
/* Set up the members list to be a null member's list */
membershead = (struct grpmember *) NULL;
}
/*
* void addmember(p)
* char *p
*
* This function adds a member to the group member's list. The
* group members list is a list of structures containing a pointer
* to the member-name and a pointer to the next item in the
* structure. The structure is not ordered in any particular way.
*
* Arguments:
* p Pointer to the member name
*
* Returns: Void
*/
static void
addmember(p)
char *p;
{
/* Automatic data */
struct grpmember *new; /* Member being added */
new = (struct grpmember *) allocblk(sizeof(struct grpmember));
new->membername = strcpy(allocstr((unsigned int) strlen(p)+1), p);
new->next = membershead;
membershead = new;
}
/*
* init isamember(p)
* char *p
*
* This function examines the list of group-members for the string
* referenced by 'p'. If 'p' is a member of the members list, the
* function returns TRUE. Otherwise it returns FALSE.
*
* Arguments:
* p Pointer to the name to search for.
*
* Returns: int
* TRUE If 'p' is found in the members list,
* FALSE otherwise
*/
static int
isamember(p)
char *p;
{
/* Automatic Data */
int found; /* TRUE if login found in list */
struct grpmember *pmem; /* Group member being examined */
/* Search the membership list for 'p' */
found = FALSE;
for (pmem = membershead ; !found && pmem ; pmem = pmem->next) {
if (strcmp(p, pmem->membername) == 0) found = TRUE;
}
return (found);
}
/*
* These functions handle the display list. The display list contains
* all of the information we're to display. The list contains a pointer
* to the login-name, a pointer to the free-field (comment), and a
* pointer to the next item in the list. The list is ordered alpha-
* betically (ascending) on the login-name field. The list initially
* contains a dummy field (to make insertion easier) that contains a
* login-name of "".
*
* Functions included:
* initdisp Initializes the display list
* adddisp Adds information to the display list
* isuidindisp Looks to see if a particular user-ID is in the
* display list
* genreport Generates a report from the items in the display
* list
* applygroup Add group information to the items in the display
* list
* applypasswd Add extended password information to the items
* in the display list
*
* Datatypes Defined:
* struct display Describes the structure that contains the information
* to be displayed. Includes pointers to the login-ID,
* free-field (comment), and the next structure in the
* list.
*
* Static Data:
* displayhead Pointer to the head of the display list. Initially
* references the null-item on the head of the list.
*/
struct display {
char *loginID; /* Login name */
char *freefield; /* Free (comment) field */
char *groupname; /* Name of the primary group */
char *iwd; /* Initial working directory */
char *shell; /* Shell after login (may be null) */
struct pwdinfo *passwdinfo; /* Password information structure */
struct secgrp *secgrplist; /* Head of the secondary group list */
uid_t userID; /* User ID */
gid_t groupID; /* Group ID of primary group */
struct display *nextlogin; /* Next login in the list */
struct display *nextuid; /* Next user-ID in the list */
};
static struct display *displayhead;
/*
* void initdisp()
*
* Initializes the display list. An empty display list contains
* a single element, the dummy element.
*
* Arguments: None
*
* Returns: Void
*/
static void
initdisp()
{
displayhead = (struct display *) allocblk(sizeof(struct display));
displayhead->nextlogin = (struct display *) NULL;
displayhead->nextuid = (struct display *) NULL;
displayhead->loginID = "";
displayhead->freefield = "";
displayhead->userID = -1;
}
/*
* void adddisp(pwent)
* struct passwd *pwent
*
* This function adds the appropriate information from the login
* description referenced by 'pwent' to the list if information
* to be displayed. It only adds the information if the login-ID
* (user-name) is unique. It inserts the information in the list
* in such a way that the list remains ordered alphabetically
* (ascending) according to the login-ID (user-name).
*
* Arguments:
* pwent Structure that contains all of the login information
* of the login being added to the list. The only
* information that this function uses is the login-ID
* (user-name) and the free-field (comment field).
*
* Returns: Void
*/
static void
adddisp(pwent)
struct passwd *pwent;
{
/* Automatic data */
struct display *new; /* Item being added to the list */
struct display *prev; /* Previous item in the list */
struct display *current; /* Next item in the list */
int found; /* FLAG, insertion point found */
int compare = 1; /* strcmp() compare value */
/* Find where this value belongs in the list */
prev = displayhead;
found = FALSE;
while (!found && (current = prev->nextlogin))
if ((compare = strcmp(current->loginID, pwent->pw_name)) >= 0) found = TRUE;
else prev = current;
/* Insert this value in the list, only if it is unique though */
if (compare != 0) {
new = (struct display *) allocblk(sizeof(struct display));
new->loginID = strcpy(allocstr((unsigned int) strlen(pwent->pw_name)+1), pwent->pw_name);
if (pwent->pw_comment && pwent->pw_comment[0] != '\0')
new->freefield =
strcpy(allocstr((unsigned int)
strlen(pwent->pw_comment)+1),
pwent->pw_comment);
else
new->freefield =
strcpy(allocstr((unsigned int)
strlen(pwent->pw_gecos)+1),
pwent->pw_gecos);
if (!pwent->pw_shell || !(*pwent->pw_shell)) new->shell = "/sbin/sh";
else new->shell = strcpy(allocstr((unsigned int) strlen(pwent->pw_shell)+1), pwent->pw_shell);
new->iwd = strcpy(allocstr((unsigned int) strlen(pwent->pw_dir)+1), pwent->pw_dir);
new->userID = pwent->pw_uid;
new->groupID = pwent->pw_gid;
new->secgrplist = (struct secgrp *) NULL;
new->passwdinfo = (struct pwdinfo *) NULL;
new->groupname = (char *) NULL;
/* Add new display item to the list ordered by login-ID */
new->nextlogin = current;
prev->nextlogin = new;
/* Find the appropriate place for the new item in the list
* ordered by userID */
prev = displayhead;
found = FALSE;
while (!found && (current = prev->nextuid))
if (current->userID > pwent->pw_uid) found = TRUE;
else prev = current;
/* Add the item into the list that is ordered by user-ID */
new->nextuid = current;
prev->nextuid = new;
}
}
/*
* int isuidindisp(pwent)
* struct passwd *pwent
*
* This function examines the display list to see if the uid in
* the (struct passwd) referenced by "pwent" is already in the
* display list. It returns TRUE if it is in the list, FALSE
* otherwise.
*
* Since the display list is ordered by user-ID, the search continues
* until a match is found or a user-ID is found that is larger than
* the one we're searching for.
*
* Arguments:
* pwent Structure that contains the user-ID we're to
* look for
*
* Returns: int
* TRUE if the user-ID was found, FALSE otherwise.
*/
static int
isuidindisp(pwent)
struct passwd *pwent; /* Struct of user to look for */
{
/* Automatic data */
struct display *dp;
/*
* Search the list, beginning at the beginning (where else?)
* and stopping when the user-ID is found or one is found that
* is greater than the user-ID we're searching for. Recall
* that this list is ordered by user-ID
*/
for (dp = displayhead->nextuid ; dp && (dp->userID < pwent->pw_uid) ; dp = dp->nextuid) ;
/* If the pointer "dp" points to a structure that has a
* matching user-ID, return TRUE. Otherwise FALSE */
return (dp && (dp->userID == pwent->pw_uid));
}
/*
* void applygroup(allgroups)
* int allgroups
*
* This function applies group information to the login-IDs in the
* display list. It always applies the primary group information.
* If "allgroups" is TRUE, it applies secondary information as well.
*
* Arguments:
* allgroups FLAG. TRUE if secondary group info is to be
* applied -- FALSE otherwise.
*
* Returns: void
*/
static void
applygroup(allgroups)
int allgroups; /* TRUE if applying secondary groups */
{
/* Automatic Data */
struct display *dp; /* Display list running ptr */
struct group *grent; /* Group info, from getgrent() */
char *p; /* Temp char pointer */
char **pp; /* Temp char * pointer */
int compare; /* Value from strcmp() */
int done; /* TRUE if finished, FALSE otherwise */
struct secgrp *psecgrp; /* Block allocated for this info */
struct secgrp *psrch; /* Secondary group -- for searching */
/* For each group-ID in the /etc/group file ... */
while (grent = getgrent()) {
/*
* Set the primary group for the login-IDs in the display
* list. For each group-ID we get, leaf through the display
* list and set the group-name if the group-IDs match
*/
p = (char *) NULL;
for (dp = displayhead->nextuid ; dp ; dp = dp->nextuid)
if ((dp->groupID == grent->gr_gid) && !dp->groupname) {
if (!p) p = strcpy(allocstr((unsigned int) strlen(grent->gr_name)+1), grent->gr_name);
dp->groupname = p;
}
/*
* If we're to be displaying secondary group membership,
* leaf through the list of group members. Then, attempt
* to find that member in the display list. When found,
* attach secondary group info to the user.
*/
if (allgroups) for (pp = grent->gr_mem ; *pp ; pp++) {
done = FALSE;
for (dp = displayhead->nextlogin ; !done && dp ; dp = dp->nextlogin) {
if (((compare = strcmp(dp->loginID, *pp)) == 0) && !(grent->gr_gid == dp->groupID)) {
if (!p) p = strcpy(allocstr((unsigned int) strlen(grent->gr_name)+1), grent->gr_name);
psecgrp = (struct secgrp *) allocblk(sizeof(struct secgrp));
psecgrp->groupID = grent->gr_gid;
psecgrp->groupname = p;
psecgrp->next = (struct secgrp *) NULL;
if (!dp->secgrplist) dp->secgrplist = psecgrp;
else {
for (psrch = dp->secgrplist ; psrch->next ; psrch = psrch->next) ;
psrch->next = psecgrp;
}
done = TRUE;
}
else if (compare > 0) done = TRUE;
}
}
}
/* Close the /etc/group file */
endgrent();
}
/*
* void applypasswd()
*
* This function applies extended password information to an item
* to be displayed. It allocates space for a structure describing
* the password, then fills in that structure from the information
* in the /etc/shadow file.
*
* Arguments: None
*
* Returns: Void
*/
static void
applypasswd()
{
/*
* Local declarations
*/
struct pwdinfo *ppasswd; /* Ptr to pwd desc in current element */
struct display *dp; /* Ptr to current element */
struct spwd *psp; /* Pointer to a shadow-file entry */
struct tm *ptm; /* Pointer to a time-of-day structure */
char *p; /* Running character pointer */
time_t pwchg; /* System-time of last pwd chg */
time_t pwexp; /* System-time of password expiration */
/* Make sure the shadow file is rewound */
setspent();
/*
* For each item in the display list...
*/
for (dp = displayhead->nextuid ; dp ; dp = dp->nextuid) {
/* Allocate structure space for the password description */
ppasswd = (struct pwdinfo *) allocblk(sizeof(struct pwdinfo));
dp->passwdinfo = ppasswd;
/*
* If there's no entry in the /etc/shadow file, assume
* that the login is locked
*/
if (!(psp = getspnam(dp->loginID))) {
pwchg = 0L; /* Epoch */
ppasswd->passwdstatus = "LK"; /* LK, Locked */
ppasswd->mindaystilchg = 0L;
ppasswd->maxdaystilchg = 0L;
ppasswd->warninterval = 0L;
ppasswd->inactive = 0L;
pwexp = 0L;
}
/*
* Otherwise, fill in the password information from the
* info in the shadow file entry
*/
else {
/* See if the login has no password */
if (!psp->sp_pwdp || !(*psp->sp_pwdp)) ppasswd->passwdstatus = "NP";
/*
* See if the login is explicitly locked (encrypted
* password is <13 characters)
*/
else if (strlen(psp->sp_pwdp) != 13) ppasswd->passwdstatus = "LK";
/*
* If it's a valid encrypted password, the login is
* password protected
*/
else {
ppasswd->passwdstatus = "PS";
for (p = psp->sp_pwdp ; *p ; p++) {
if (!isalnum(*p) && (*p != '.') && (*p != '/')) {
ppasswd->passwdstatus = "LK";
break;
}
}
}
/*
* Set up the last-changed date, the minimum days between
* changes, the maximum life of a password, the interval
* before expiration that the user is warned, the number of
* days a login can be inactive before it expires, and the
* login expiration date
*/
pwchg = psp->sp_lstchg;
ppasswd->mindaystilchg = psp->sp_min;
ppasswd->maxdaystilchg = psp->sp_max;
ppasswd->warninterval = psp->sp_warn;
ppasswd->inactive = psp->sp_inact;
pwexp = psp->sp_expire;
}
/*
* Convert the date of the last password change from days-
* since-epoch to mmddyy (integer) form. Involves the
* intermediate step of converting the date from days-
* since-epoch to seconds-since-epoch. We'll set this to
* somewhere near the middle of the day, since there are not
* always 24*60*60 seconds in a year. (Yeech)
*
* Note: The form mmddyy should probably be subject to
* internationalization -- Non-Americans will think that
* 070888 is 07 August 88 when the software is trying to say
* 08 July 88. Systems Engineers seem to think that this isn't
* a problem though...
*/
if (pwchg != -1L) {
pwchg = (pwchg * DAY) + (DAY/2);
ptm = localtime(&pwchg);
ppasswd->datechg = ((long) (ptm->tm_mon+1) * 10000L) +
(long) ((ptm->tm_mday * 100) +
(ptm->tm_year % 100));
} else ppasswd->datechg = 0L;
/*
* Convert the passwd expiration date from days-since-epoch
* to mmddyy (long integer) form using the same algorithm and
* comments as above.
*/
if (pwexp != -1L) {
pwexp = (pwexp * DAY) + (DAY/2);
ptm = localtime(&pwexp);
ppasswd->expdate = ((long) (ptm->tm_mon+1) * 10000L) +
(long) ((ptm->tm_mday * 100) +
(ptm->tm_year % 100));
} else ppasswd->expdate = 0L;
}
/* Close the shadow password file */
endspent();
}
/*
* int hasnopasswd(pwent)
* struct passwd *pwent
*
* This function examines a login's password-file entry
* and, if necessary, its shadow password-file entry and
* returns TRUE if that user-ID has no password, meaning
* that the user-ID can be used to log into the system
* without giving a password. The function returns FALSE
* otherwise.
*
* Arguments:
* pwent Login to examine.
*
* Returns: int
* TRUE if the login can be used without a password, FALSE
* otherwise.
*/
static int
hasnopasswd(pwent)
struct passwd *pwent; /* /etc/passwd entry of login to check */
{
/* Local definitions */
struct spwd *psp; /* /etc/shadow file struct */
int nopwflag; /* TRUE if login has no passwd */
/*
* A login has no password if:
* 1. There exists an entry for that login in the
* shadow password-file (/etc/passwd), and
* 2. The encrypted password in the structure describing
* that entry is either:
* a. (char *) NULL
* b. A null string ("")
*/
/* Get the login's entry in the shadow password file */
if (psp = getspnam(pwent->pw_name)) {
/* Look at the encrypted password in that entry */
if (psp->sp_pwdp == (char *)0 ||
*psp->sp_pwdp == '\0')
nopwflag = TRUE;
else
nopwflag = FALSE;
}
else
nopwflag = FALSE;
/* Done ... */
return(nopwflag);
}
/*
* void writeunformatted(current, xtndflag, expflag)
* struct display *current
* int xtndflag
* int expflag
*
* This function writes the data in the display structure "current"
* to the standard output file. It writes the information in the
* form of a colon-list. It writes secondary group information if
* that information is in the structure, it writes extended
* (initial working directory, shell, and password-aging) info
* if the "xtndflag" is TRUE, and it writes password expiration
* information if "expflag" is TRUE.
*
* Arguments:
* current Structure containing information to write.
* xtndflag TRUE if extended information is to be written,
* FALSE otherwise
* expflag TRUE if password expiration information is to
* be written, FALSE otherwise
*
* Returns: void
*/
static void
writeunformatted(current, xtndflag, expflag)
struct display *current; /* Struct with info to write */
int xtndflag; /* Write extended output flag */
int expflag; /* Write password expiration info flag */
{
/* Automatic data */
struct secgrp *psecgrp; /* Secondary group info */
struct pwdinfo *pwdinfo; /* Password aging info */
/* Write the general information */
(void) fprintf(stdout, "%s:%ld:%s:%ld:%s",
current->loginID,
current->userID,
current->groupname == (char *) NULL ? "" : current->groupname,
current->groupID,
current->freefield);
/*
* If the group information is there, write it (it's only
* there if it's supposed to be written)
*/
for (psecgrp = current->secgrplist ; psecgrp ; psecgrp = psecgrp->next)
(void) fprintf(stdout, ":%s:%ld", psecgrp->groupname, psecgrp->groupID);
/* If the extended info flag is TRUE, write the extended information */
if (xtndflag) {
pwdinfo = current->passwdinfo;
(void) fprintf(stdout, ":%s:%s:%s:%6.6ld:%ld:%ld:%ld",
current->iwd, current->shell,
pwdinfo->passwdstatus,
pwdinfo->datechg,
pwdinfo->mindaystilchg, pwdinfo->maxdaystilchg,
pwdinfo->warninterval);
}
/* If the password expiration information is requested, write it. */
if (expflag) {
pwdinfo = current->passwdinfo;
(void) fprintf(stdout, ":%ld:%ld", pwdinfo->inactive, pwdinfo->expdate);
}
/* Terminate the information with a new-line */
(void) putc('\n', stdout);
}
/*
* void writeformatted(current, xtndflag, expflag)
* struct display *current
* int xtndflag
* int expflag
*
* This function writes the data in the display structure "current"
* to the standard output file. It writes the information in an
* easily readable format. It writes secondary group information
* if that information is in the structure, it writes extended
* (initial working directory, shell, and password-aging) info if
* "xtndflag" is TRUE, and it write password expiration information
* if "expflag" is TRUE.
*
* Arguments:
* current Structure containing info to write.
* xtndflag TRUE if extended information is to be written,
* FALSE otherwise
* expflag TRUE if password expiration information is to be written,
* FALSE otherwise
*
* Returns: void
*/
static void
writeformatted(current, xtndflag, expflag)
struct display *current; /* Struct with info to write */
int xtndflag; /* Write extended output flag */
int expflag; /* Write password expiration info flag */
{
/* Automatic data */
struct secgrp *psecgrp; /* Secondary group info */
struct pwdinfo *pwdinfo; /* Password aging info */
/* Write general information */
(void) fprintf(stdout, "%-14s %-6ld %-14s %-6ld %s\n",
current->loginID, current->userID,
current->groupname == (char *) NULL ? "" : current->groupname,
current->groupID, current->freefield);
/*
* Write information about secondary groups if the info exists
* (it only exists if it is to be written)
*/
for (psecgrp = current->secgrplist ; psecgrp ; psecgrp = psecgrp->next)
(void) fprintf(stdout, " %-14s %-6ld\n",
psecgrp->groupname, psecgrp->groupID);
/* If the extended information flag is TRUE, write the extended information */
if (xtndflag) {
pwdinfo = current->passwdinfo;
(void) fprintf(stdout, " %s\n", current->iwd);
(void) fprintf(stdout, " %s\n", current->shell);
(void) fprintf(stdout, " %s %6.6ld %ld %ld %ld\n",
pwdinfo->passwdstatus,
pwdinfo->datechg, pwdinfo->mindaystilchg,
pwdinfo->maxdaystilchg,
pwdinfo->warninterval);
}
/* If the password expiration info flag is TRUE, write that information */
if (expflag) {
pwdinfo = current->passwdinfo;
(void) fprintf(stdout, " %ld %6.6ld\n",
pwdinfo->inactive, pwdinfo->expdate);
}
}
/*
* void genuidreport(pipeflag, xtndflag, expflag)
* int pipeflag
* int xtndflag
* int expflag
*
* This function generates a report on the standard output
* stream (stdout) containing the login-IDs in the list of
* logins built by this command. The list is ordered based
* on user-ID. If the <pipeflag> variable is not zero, it
* will generate a report containing parsable records.
* Otherwise, it will generate a columnarized report. If
* the <xtndflag> variable is not zero, it will include the
* extended set of information (password aging info, home
* directory, shell process, etc.). If <expflag> is not
* zero, it will display password expiration information.
*
* Arguments:
* pipeflag int
* TRUE if a parsable report is needed,
* FALSE if a columnar report is needed
* xtndflag int
* TRUE if extended set of info is to be displayed,
* FALSE otherwise
* expflag int
* TRUE if password expiration information is to be
* displayed, FALSE otherwise
*
* Returns: void
*/
static void
genuidreport(pipeflag, xtndflag, expflag)
int pipeflag; /* Parsible output flag */
int xtndflag; /* Extended info flag */
int expflag; /* Password expiration info flag */
{
/* Automatic data */
struct display *current; /* Data being displayed */
/*
* Initialization for loop.
* (NOTE: The first element in the list of logins to
* display is a dummy element.)
*/
current = displayhead;
/*
* Display elements in the list
*/
if (pipeflag)
for (current = displayhead->nextuid ; current ; current = current->nextuid)
writeunformatted(current, xtndflag, expflag);
else
for (current = displayhead->nextuid ; current ; current = current->nextuid)
writeformatted(current, xtndflag, expflag);
}
/*
* void genlogreport(pipeflag, xtndflag, expflag)
* int pipeflag
* int xtndflag
* int expflag
*
* This function generates a report on the standard output
* stream (stdout) containing the login-IDs in the list of
* logins built by this command. The list is ordered based
* on user name. If the <pipeflag> variable is not zero, it
* will generate a report containing parsable records.
* Otherwise, it will generate a columnarized report. If
* the <xtndflag> variable is not zero, it will include the
* extended set of information (password aging info, home
* directory, shell process, etc.). If <expflag> is not
* zero, it will include password expiration information.
*
* Arguments:
* pipeflag int
* TRUE if a parsable report is needed,
* FALSE if a columnar report is needed
* xtndflag int
* TRUE if extended set of info is to be displayed,
* FALSE otherwise
* expflag int
* TRUE if password expiration information is to
* be displayed, FALSE otherwise
*
* Returns: void
*/
static void
genlogreport(pipeflag, xtndflag, expflag)
int pipeflag; /* Parsable output flag */
int xtndflag; /* Extended info flag */
int expflag; /* Password expiration info flag */
{
/* Automatic data */
struct display *p; /* Value being displayed */
/*
* Initialization for loop.
* (NOTE: The first element in the list of logins to
* display is a dummy element.)
*/
p = displayhead;
/*
* Display elements in the list
*/
if (pipeflag)
for (p = displayhead->nextlogin ; p ; p = p->nextlogin)
writeunformatted(p, xtndflag, expflag);
else
for (p = displayhead->nextlogin ; p ; p = p->nextlogin)
writeformatted(p, xtndflag, expflag);
}
char *
strcpmalloc(str)
char *str;
{
char *cp;
if (str == NULL)
return NULL;
return (strcpy(allocstr((unsigned int)strlen(str)+1), str));
}
struct localpw {
struct localpw *next;
struct passwd pw;
};
struct localpw *pwtable = NULL;
struct localpw *pwptr; /* Local passwd pointer for getpwent()
* -- -1 means not in use, NULL for EOF */
int in_localgetpwent = 0; /* Set if in local_getpwent */
void
build_localpw()
{
struct localpw *next, *cur;
struct passwd *pw;
next = (struct localpw *) allocblk(sizeof (struct localpw));
pwtable = next;
while ((pw = getpwent()) != NULL) {
/*
* Copy the data -- we have to alloc areas for it all
*/
next->pw.pw_name = strcpmalloc(pw->pw_name);
next->pw.pw_passwd = strcpmalloc(pw->pw_passwd);
next->pw.pw_uid = pw->pw_uid;
next->pw.pw_gid = pw->pw_gid;
next->pw.pw_age = strcpmalloc(pw->pw_age);
next->pw.pw_comment = strcpmalloc(pw->pw_comment);
next->pw.pw_gecos = strcpmalloc(pw->pw_gecos);
next->pw.pw_dir = strcpmalloc(pw->pw_dir);
next->pw.pw_shell = strcpmalloc(pw->pw_shell);
next->next = (struct localpw *) allocblk(sizeof (struct localpw));
cur = next;
next = next->next;
}
/*
* At this point we have one extra (struct localpw) allocated;
* sine alloclbk doesn't have a freeblk, we just leave it unreferenced.
*/
if (pwtable == next)
pwtable = NULL;
else
cur->next = NULL;
endpwent();
}
struct passwd *
local_getpwent()
{
if (!in_localgetpwent) {
in_localgetpwent = 1;
pwptr = pwtable;
} else if ( pwptr != NULL)
pwptr = pwptr->next;
if (pwptr != NULL)
return &(pwptr->pw);
else
return NULL;
}
void
local_endpwent()
{
in_localgetpwent = 0;
}
long
local_pwtell()
{
return (long)pwptr;
}
void
local_pwseek(ptr)
long ptr;
{
pwptr = (struct localpw *)ptr;
}
/*
* logins [-admopstux] [-l logins] [-g groups]
*
* This command generates a report of logins administered on
* the system. The list will contain logins that meet criteria
* described by the options in the list. If there are no options,
* it will list all logins administered. It is intended to be used
* only by administrators.
*
* Options:
* -a Display password expiration information.
* -d list all logins that share user-IDs with another
* login.
* -g groups specifies the names of the groups to which a login
* must belong before it is included in the generated
* list. "groups" is a comma-list of group names.
* -l logins specifies the logins to display. "logins" is a
* comma-list of login names.
* -m in addition to the usual information, for each
* login displayed, list all groups to which that
* login is member.
* -o generate a report as a colon-list instead of in a
* columnar format
* -p list all logins that have no password.
* -s list all system logins
* -t sort the report lexicographically by login name
* instead of by user-ID
* -u list all user logins
* -x in addition to the usual information, display an
* extended set of information that includes the home
* directory, initial process, and password status and
* aging information
*
* Exit Codes:
* 0 All's well that ends well
* 1 Usage error
*/
main(argc, argv)
int argc; /* Number of args on the command line */
char *argv[]; /* Pointers pointing to the arguments */
{
/* Automatic data */
struct passwd *plookpwd; /* Ptr to searcher pw (-d) */
struct reqgrp *reqgrphead; /* Head of the req'd group list */
struct reqgrp *pgrp; /* Current item in req'd group list */
struct reqgrp *qgrp; /* Prev item in the req'd group list */
struct reqlogin *reqloginhead; /* Head of req'd login list */
struct reqlogin *plogin; /* Current item in the req'd login list */
struct reqlogin *qlogin; /* Prev item in the req'd login list */
struct passwd *pwent; /* /etc/passwd entry */
struct group *grent; /* /etc/group entry */
char *token; /* Token extracted by strtok() */
char **pp; /* Group member */
char *g_arg; /* -g option's argument */
char *l_arg; /* -l option's argument */
long lookpos; /* File pos'n, rec we're looking for */
int a_seen; /* Is -a requested? */
int d_seen; /* Is -d requested? */
int g_seen; /* Is -g requested? */
int l_seen; /* Is -l requested? */
int m_seen; /* Is -m requested? */
int o_seen; /* Is -o requested? */
int p_seen; /* Is -p requested? */
int s_seen; /* Is -s requested? */
int t_seen; /* Is -t requested? */
int u_seen; /* Is -u requested? */
int x_seen; /* Is -x requested? */
int errflg; /* Is there a command-line problem */
int done; /* Is the process (?) is complete */
int groupcount; /* Number of groups specified by the user */
int doall; /* Are all logins to be reported */
int c; /* Character returned from getopt() */
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
/* Initializations */
initmsg(argv[0]);
/*
* Command-line processing
*/
/* Initializations */
a_seen = FALSE;
d_seen = FALSE;
g_seen = FALSE;
l_seen = FALSE;
m_seen = FALSE;
o_seen = FALSE;
p_seen = FALSE;
s_seen = FALSE;
t_seen = FALSE;
u_seen = FALSE;
x_seen = FALSE;
errflg = FALSE;
opterr = 0;
while (!errflg && ((c = getopt(argc, argv, OPTSTR)) != EOF)) {
/* Case on the option character */
switch(c) {
/*
* -a option:
* Display password expiration information
*/
case 'a':
if (a_seen) errflg = TRUE;
else a_seen = TRUE;
break;
/*
* -d option:
* Display logins which share user-IDs with other logins
*/
case 'd':
if (d_seen) errflg = TRUE;
else d_seen = TRUE;
break;
/*
* -g <groups> option:
* Display the specified groups
*/
case 'g':
if (g_seen) errflg = TRUE;
else {
g_seen = TRUE;
g_arg = optarg;
}
break;
/*
* -l <logins> option:
* Display the specified logins
*/
case 'l':
if (l_seen) errflg = TRUE;
else {
l_seen = TRUE;
l_arg = optarg;
}
break;
/*
* -m option:
* Display multiple group information
*/
case 'm':
if (m_seen) errflg = TRUE;
else m_seen = TRUE;
break;
/*
* -o option:
* Display information as a colon-list
*/
case 'o':
if (o_seen) errflg = TRUE;
else o_seen = TRUE;
break;
/*
* -p option:
* Select logins that have no password
*/
case 'p':
if (p_seen) errflg = TRUE;
else p_seen = TRUE;
break;
/*
* -s option:
* Select system logins
*/
case 's':
if (s_seen) errflg = TRUE;
else s_seen = TRUE;
break;
/*
* -t option:
* Sort alphabetically by login-ID instead of numerically
* by user-ID
*/
case 't':
if (t_seen) errflg = TRUE;
else t_seen = TRUE;
break;
/*
* -u option:
* Select user logins
*/
case 'u':
if (u_seen) errflg = TRUE;
else u_seen = TRUE;
break;
/*
* -x option:
* Display extended info (init working dir, shell, pwd info)
*/
case 'x':
if (x_seen) errflg = TRUE;
else x_seen = TRUE;
break;
default: /* Oops.... */
errflg = TRUE;
}
}
/* Write out a usage message if necessary and quit */
if (errflg || (optind != argc)) {
wrtmsg(MM_ERROR, MM_NULLACT, MM_NULLTAG, gettext(USAGE_MSG));
exit(1);
}
/*
* The following section does preparation work, setting up for
* building the list of logins to display
*/
/*
* Very first thing, build an in-core structure of passwd file entries.
* This is important since we have to assume that getpwent() is going
* out to one or more network name services that could be changing
* on the fly. This will limit us to one pass through the network data.
*/
build_localpw();
/*
* If the -g groups option was on the command line, build a
* list containing groups we're to list logins for.
*/
if (g_seen) {
groupcount = 0;
reqgrphead = (struct reqgrp *) NULL;
if (token = strtok(g_arg, ",")) {
pgrp = (struct reqgrp *) allocblk(sizeof(struct reqgrp));
pgrp->groupname = token;
pgrp->found = FALSE;
pgrp->next = (struct reqgrp *) NULL;
groupcount++;
reqgrphead = pgrp;
qgrp = pgrp;
while (token = strtok(NULL, ",")) {
pgrp = (struct reqgrp *) allocblk(sizeof(struct reqgrp));
pgrp->groupname = token;
pgrp->found = FALSE;
pgrp->next = (struct reqgrp *) NULL;
groupcount++;
qgrp->next = pgrp;
qgrp = pgrp;
}
}
}
/*
* If -l logins is on the command line, build a list of
* logins we're to generate reports for.
*/
if (l_seen) {
reqloginhead = (struct reqlogin *) NULL;
if (token = strtok(l_arg, ",")) {
plogin = (struct reqlogin *) allocblk(sizeof(struct reqlogin));
plogin->loginname = token;
plogin->found = FALSE;
plogin->next = (struct reqlogin *) NULL;
reqloginhead = plogin;
qlogin = plogin;
while (token = strtok(NULL, ",")) {
plogin = (struct reqlogin *) allocblk(sizeof(struct reqlogin));
plogin->loginname = token;
plogin->found = FALSE;
plogin->next = (struct reqlogin *) NULL;
qlogin->next = plogin;
qlogin = plogin;
}
}
}
if (l_seen) {
while(pwent = local_getpwent()) {
done = FALSE;
for (plogin = reqloginhead ; !done && plogin ;
plogin = plogin->next) {
if (strcmp(pwent->pw_name,
plogin->loginname) == 0) {
plogin->found = TRUE;
done = TRUE;
}
}
}
local_endpwent();
}
/*
* Generate the list of login information to display
*/
/* Initialize the login list */
initmembers();
/*
* If -g groups was specified, generate a list of members
* of the specified groups
*/
if (g_seen) {
/* For each group in the /etc/group file ... */
while (grent = getgrent()) {
/* For each group mentioned with the -g option ... */
for (pgrp = reqgrphead ; (groupcount > 0) && pgrp ; pgrp = pgrp->next) {
if (!pgrp->found) {
/*
* If the mentioned group is found in the
* /etc/group file ...
*/
if (strcmp(grent->gr_name, pgrp->groupname) == 0) {
/*
* Mark the entry is found, remembering the
* group-ID for later
*/
pgrp->found = TRUE;
groupcount--;
pgrp->groupID = grent->gr_gid;
for (pp = grent->gr_mem ; *pp ; pp++) addmember(*pp);
}
}
}
}
/*
* If any groups weren't found, write a message indicating
* such, then continue
*/
qgrp = (struct reqgrp *) NULL;
for (pgrp = reqgrphead ; pgrp ; pgrp = pgrp->next) {
if (!pgrp->found) {
wrtmsg(MM_WARNING, MM_NULLACT, MM_NULLTAG, gettext("%s was not found"), pgrp->groupname);
if (!qgrp) reqgrphead = pgrp->next;
else qgrp->next = pgrp->next;
}
else qgrp = pgrp;
}
endgrent();
}
/* Initialize the list of logins to display */
initdisp();
/*
* Add logins that have user-IDs that are used more than once,
* if requested. This command is pretty slow, since the algorithm
* reads from the /etc/passwd file 1+2+3+...+n times where n is the
* number of login-IDs in the /etc/passwd file. (Actually, this
* can be optimized so it's not quite that bad, but the order or
* magnitude stays the same.)
*
* Note: This processing needs to be done before any other options
* are processed -- the algorithm contains an optimization
* that insists on the display list being empty before this
* option is processed.
*/
if (d_seen) {
/*
* The following code is a quick&dirty reimplementation of the
* original algorithm, which opened the password file twice (to
* get two file pointer into the data) and then used fgetpwent()
* in undocumented ways to scan through the file, checking for
* duplicates. This does not work when getpwent() is used to
* go out over the network, since there is not file pointer.
*
* Instead an in-memory list of passwd structures is built, and then
* this list is scanned. The routines Local_getpwent(), etc.,
* are designed to mimic the standard library routines, so this code
* does not have to be extensively modified.
*/
/*
* For reference, here is the original comment about the next
* section of code. Some of the code has changed, but the algorithm
* is the same:
*
* Open the system password file once. This instance will be
* used to leaf through the file once, reading each entry once,
* and searching the remainder of the file for another login-ID
* that has the same user-ID. Note that there are lots of
* contortions one has to go through when reading two instances
* of the /etc/passwd file. That's why there's some seeking,
* re-reading of the same record, and other junk. Luckily, this
* feature won't be requested very often, and still isn't too
* slow...
*/
/* For each entry in the passwd database ... */
while (plookpwd = local_getpwent()) {
/*
* Optimization -- If the login's user-ID is already in
* the display list, there's no reason to process this
* entry -- it's already there.
*/
if (!isuidindisp(plookpwd)) {
/*
* Rememeber the current entry's position, so when we finish
* scanning through the database looking for duplicates
* we can return to the current place, so that the enclosing
* loop will march in an orderly fashion through the passwd
* database.
*/
done = FALSE;
lookpos = local_pwtell();
/*
* For each record in the passwd database beyond
* the searching record ...
*/
while (pwent = local_getpwent()) {
/*
* If there's a match between the searcher's user-
* ID and the searchee's user-ID ...
*/
if (pwent->pw_uid == plookpwd->pw_uid) {
/*
* If this is the first duplicate of this searcher
* that we find,
* add the searcher's record to the display list
* (It wants to be on the list first
* to avoid ordering "flakeyness")
*/
if (done == FALSE) {
adddisp(plookpwd);
done == TRUE;
}
/*
* Now add the searchee's record
*/
adddisp(pwent);
}
}
/* Reposition to searcher record */
local_pwseek(lookpos);
}
}
local_endpwent();
}
/*
* Loop through the passwd database squirelling away the
* information we need for the display.
*
* NOTE: Once a login is added to the list, the rest of the
* body of the loop is bypassed (via a continue statement).
*/
doall = !(s_seen || u_seen || p_seen || d_seen || l_seen || g_seen);
if (doall || s_seen || u_seen || p_seen || l_seen || g_seen) {
while (pwent = local_getpwent()) {
done = FALSE;
/* If no user-specific options were specified,
* include this login-ID */
if (doall) {
adddisp(pwent);
continue;
}
/* If the user specified system login-IDs,
* and this is a system ID, include it */
if (s_seen) if (isasystemlogin(pwent)) {
adddisp(pwent);
continue;
}
/* If the user specified user login-IDs,
* and this is a user ID, include it */
if (u_seen) if (isauserlogin(pwent)) {
adddisp(pwent);
continue;
}
/* If the user is asking for login-IDs that have
* no password, and this one has no password,
* include it */
if (p_seen) if (hasnopasswd(pwent)) {
adddisp(pwent);
continue;
}
/*
* If specific logins were requested, leaf through
* the list of logins they requested. If this login
* is on the list, include it.
*/
if (l_seen) {
for (plogin = reqloginhead ; !done && plogin ; plogin = plogin->next) {
if (strcmp(pwent->pw_name, plogin->loginname) == 0) {
plogin->found = TRUE;
adddisp(pwent);
done = TRUE;
}
}
if (done) continue;
}
/*
* If specific groups were requested, leaf through the
* list of login-IDs that belong to those groups. If this
* login-ID is in that list, or its primary group is one
* of those requested, include it.
*/
if (g_seen) {
for (pgrp = reqgrphead ; !done && pgrp ; pgrp = pgrp->next)
if (pwent->pw_gid == pgrp->groupID) {
adddisp(pwent);
done = TRUE;
}
if (!done && isamember(pwent->pw_name)) {
adddisp(pwent);
done = TRUE;
}
}
if (done) continue;
}
local_endpwent();
}
/* Let the user know about logins they requested that
* don't exist */
if (l_seen) for (plogin = reqloginhead ; plogin ; plogin = plogin->next)
if (!plogin->found)
wrtmsg(MM_WARNING, MM_NULLACT, MM_NULLTAG, gettext("%s was not found"), plogin->loginname);
/*
* Apply group information
*/
applygroup(m_seen);
/*
* Apply password information (only needed if the extended
* set of information has been requested)
*/
if (x_seen || a_seen) applypasswd();
/*
* Generate a report from this display items we've squirreled
* away
*/
if (t_seen) genlogreport(o_seen, x_seen, a_seen);
else genuidreport(o_seen, x_seen, a_seen);
/*
* We're through!
*/
exit(0);
#ifdef lint
return(0);
#endif
}