logins.c revision b816ddf83939c2b433da956720fad32dfb172096
/*
* 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
* 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 2005 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.
*/
#include <stdio.h>
#include <stdlib.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>
/*
* 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.
*/
/*
* 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,
* 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 */
};
/* Describes a specified login name (from the -l logins option) */
struct reqlogin {
char *loginname; /* Requested login name */
};
/*
* 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 */
};
/*
* 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
*/
/*
* 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(char *p)
{
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 */
/* 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
*/
/*PRINTFLIKE4*/
static void
{
int errorflg; /* TRUE if problem generating message */
/* No problems yet */
/* Generate the error message */
if (text != MM_NULLTXT) {
}
/*
* 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) {
gettext("Internal message buffer overflow"),
exit(100);
}
}
/*
* These functions control the group membership list, as found in
*
* Functions included:
* 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;
};
static struct grpmember *membershead;
/*
* 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(char *p)
{
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(char *p)
{
int found; /* TRUE if login found in list */
/* Search the membership list for 'p' */
}
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) */
};
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(void)
{
}
/*
* 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
{
int found; /* FLAG, insertion point found */
/* Find where this value belongs in the list */
prev = displayhead;
} else {
}
}
/* Insert this value in the list, only if it is unique though */
if (compare != 0) {
} else {
}
} else {
}
/* Add new display item to the list ordered by login-ID */
/*
* Find the appropriate place for the new item in the list
* ordered by userID
*/
prev = displayhead;
} else {
}
}
/* Add the item into the list that is ordered by user-ID */
}
}
/*
* 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
{
/*
* 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
*/
continue;
}
/*
* If the pointer "dp" points to a structure that has a
* matching user-ID, return TRUE. Otherwise FALSE
*/
}
/*
* 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(int allgroups)
{
char *p; /* Temp char pointer */
char **pp; /* Temp char * pointer */
int compare; /* Value from strcmp() */
int done; /* TRUE if finished, FALSE otherwise */
if (!allgroups) {
/* short circute getting all the groups */
}
}
return;
}
/*
* 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 = NULL;
if (!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.
*/
*pp)) == 0) &&
if (!p) {
}
sizeof (struct secgrp));
if (!dp->secgrplist) {
} else {
continue;
}
}
} else if (compare > 0) {
}
}
}
}
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
*
* Arguments: None
*
* Returns: Void
*/
static void
applypasswd(void)
{
/* Make sure the shadow file is rewound */
setspent();
/*
* For each item in the display list...
*/
/* Allocate structure space for the password description */
/*
* that the login is locked
*/
pwchg = 0L; /* Epoch */
ppasswd->mindaystilchg = 0L;
ppasswd->maxdaystilchg = 0L;
ppasswd->warninterval = 0L;
pwexp = 0L;
} else {
/*
* Otherwise, fill in the password information from the
* info in the shadow entry
*/
sizeof (LOCKSTRING)-1) == 0)
sizeof (NOLOGINSTRING)-1) == 0)
else
/*
* 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
*/
}
/*
* 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) {
} else {
}
/*
* 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) {
} else {
}
}
/* 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
{
int nopwflag; /* TRUE if login has no passwd */
/*
* A login has no password if:
* 1. There exists an entry for that login in the
* 2. The encrypted password in the structure describing
* that entry is either: NULL or a null string ("")
*/
/* Get the login's entry in the shadow password file */
/* Look at the encrypted password in that entry */
} else {
}
} else {
}
/* 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
{
/* Write the general information */
/*
* If the group information is there, write it (it's only
* there if it's supposed to be written)
*/
}
/* If the extended info flag is TRUE, write the extended information */
if (xtndflag) {
}
/* If the password expiration information is requested, write it. */
if (expflag) {
}
/* Terminate the information with a new-line */
}
/*
* 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 to be written,
* FALSE otherwise
* expflag TRUE if password expiration information to be written,
* FALSE otherwise
*
* Returns: void
*/
static void
{
/* Write general information */
/*
* Write information about secondary groups if the info exists
* (it only exists if it is to be written)
*/
}
/*
* If the extended information flag is TRUE,
* write the extended information
*/
if (xtndflag) {
"%6.6ld %ld %ld %ld\n",
}
/*
* If the password expiration info flag is TRUE,
* write that information
*/
if (expflag) {
}
}
/*
* 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
{
/*
* Initialization for loop.
* (NOTE: The first element in the list of logins to display is
* a dummy element.)
*/
/*
* Display elements in the list
*/
if (pipeflag) {
}
} else {
}
}
}
/*
* 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
{
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) {
}
} else {
}
}
}
struct localpw {
};
/* Local passwd pointer for getpwent() -- -1 means not in use, NULL for EOF */
int in_localgetpwent = 0; /* Set if in local_getpwent */
static struct localpw *
/*
* Copy the data -- we have to alloc areas for it all
*/
return (cur);
}
void
{
/*
* Copy the data -- we have to alloc areas for it all
*/
}
}
/*
* Copy the data -- we have to alloc areas for it all
*/
}
}
} else {
}
endpwent();
}
struct passwd *
local_getpwent(void)
{
if (!in_localgetpwent) {
in_localgetpwent = 1;
}
else
return (NULL);
}
void
local_endpwent(void)
{
in_localgetpwent = 0;
}
long
local_pwtell(void)
{
return ((long)pwptr);
}
void
local_pwseek(long 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
*/
int
{
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 */
int doall; /* Are all logins to be reported */
int c; /* Character returned from getopt() */
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
/* Initializations */
/* Command-line processing */
/* Initializations */
opterr = 0;
/* Case on the option character */
switch (c) {
/*
* -a option:
* Display password expiration information
*/
case 'a':
if (a_seen)
else
break;
/*
* -d option:
* Display logins which share user-IDs with other logins
*/
case 'd':
if (d_seen)
else
break;
/*
* -g <groups> option:
* Display the specified groups
*/
case 'g':
if (g_seen) {
} else {
}
break;
/*
* -l <logins> option:
* Display the specified logins
*/
case 'l':
if (l_seen) {
} else {
}
break;
/*
* -m option:
* Display multiple group information
*/
case 'm':
if (m_seen)
else
break;
/*
* -o option:
* Display information as a colon-list
*/
case 'o':
if (o_seen)
else
break;
/*
* -p option:
* Select logins that have no password
*/
case 'p':
if (p_seen)
else
break;
/*
* -s option:
* Select system logins
*/
case 's':
if (s_seen)
else
break;
/*
* -t option:
* Sort alphabetically by login-ID instead of numerically
* by user-ID
*/
case 't':
if (t_seen)
else
break;
/*
* -u option:
* Select user logins
*/
case 'u':
if (u_seen)
else
break;
/*
* -x option:
* Display extended info (init working dir, shell, pwd info)
*/
case 'x':
if (x_seen)
else
break;
default: /* Oops.... */
}
}
/* Write out a usage message if necessary and quit */
exit(1);
}
/*
* The following section does preparation work, setting up for
* building the list of logins to display
*/
/*
* If -l logins is on the command line, build a list of
* logins we're to generate reports for.
*/
if (l_seen) {
reqloginhead = NULL;
}
}
/*
* Build an in-core structure of just the passwd database
* entries requested. This greatly reduces the time
* to get all entries and filter later.
*/
} else {
/*
* Build an in-core structure of all passwd database
* 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.
*/
}
/*
* 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 = NULL;
groupcount++;
reqgrphead = pgrp;
groupcount++;
}
}
}
/*
* Generate the list of login information to display
*/
/* Initialize the login list */
membershead = NULL;
/*
* If -g groups was specified, generate a list of members
* of the specified groups
*/
if (g_seen) {
/* For each group mentioned with the -g option ... */
/*
* Remembering the group-ID for later
*/
groupcount--;
}
} else {
gettext("%s was not found"),
}
}
}
/* 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
* 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
* 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.
*/
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 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")
*/
}
/*
* Now add the searchee's
* record
*/
}
}
/* Reposition to searcher record */
}
}
}
/*
* 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).
*/
while (pwent = local_getpwent()) {
/*
* If no user-specific options were specified,
* include this login-ID
*/
if (doall) {
continue;
}
/*
* If the user specified system login-IDs,
* and this is a system ID, include it
*/
if (s_seen) {
if (isasystemlogin(pwent)) {
continue;
}
}
/*
* If the user specified user login-IDs,
* and this is a user ID, include it
*/
if (u_seen) {
if (isauserlogin(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)) {
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) {
}
}
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) {
}
}
}
}
if (done)
continue;
}
}
/* Let the user know about logins they requested that don't exist */
if (l_seen) {
gettext("%s was not found"),
}
}
}
/* Apply group information */
/*
* Apply password information (only needed if the extended
* set of information has been requested)
*/
applypasswd();
/*
* Generate a report from this display items we've squirreled away
*/
if (t_seen)
else
/* We're through! */
return (0);
}