summaryrefslogtreecommitdiff
path: root/usr/src/cmd/logins
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/logins
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/logins')
-rw-r--r--usr/src/cmd/logins/Makefile49
-rw-r--r--usr/src/cmd/logins/logins.c1974
-rw-r--r--usr/src/cmd/logins/logins.xcl21
-rw-r--r--usr/src/cmd/logins/pkginfo33
4 files changed, 2077 insertions, 0 deletions
diff --git a/usr/src/cmd/logins/Makefile b/usr/src/cmd/logins/Makefile
new file mode 100644
index 0000000000..5fd1f63cc9
--- /dev/null
+++ b/usr/src/cmd/logins/Makefile
@@ -0,0 +1,49 @@
+#
+# 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
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 1990 by Sun Microsystems, Inc.
+#
+# cmd/logins/Makefile
+#
+
+PROG= logins
+PKGINFO= pkginfo
+TXTS= $(PKGINFO)
+
+include ../Makefile.cmd
+TEXT_DOMAIN= SUNW_OST_ADMIN
+
+FILEMODE= 0750
+
+.KEEP_STATE:
+
+all: $(PROG) $(TXTS)
+
+install: all $(ROOTPROG)
+
+clean:
+
+lint: lint_PROG
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/logins/logins.c b/usr/src/cmd/logins/logins.c
new file mode 100644
index 0000000000..2d72be24a6
--- /dev/null
+++ b/usr/src/cmd/logins/logins.c
@@ -0,0 +1,1974 @@
+/*
+ * 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
+}
diff --git a/usr/src/cmd/logins/logins.xcl b/usr/src/cmd/logins/logins.xcl
new file mode 100644
index 0000000000..f2e56d8dea
--- /dev/null
+++ b/usr/src/cmd/logins/logins.xcl
@@ -0,0 +1,21 @@
+#
+# 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
+#
diff --git a/usr/src/cmd/logins/pkginfo b/usr/src/cmd/logins/pkginfo
new file mode 100644
index 0000000000..c5683498ef
--- /dev/null
+++ b/usr/src/cmd/logins/pkginfo
@@ -0,0 +1,33 @@
+# 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 1992 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.1 */
+#
+# This is the package info file for
+# the user command "logins" in the user administration display facility.
+#
+#
+PKG="cfg"
+NAME="Configuration Display"
+VERSION="0.0"
+CATEGORY="system"