xref: /onnv-gate/usr/src/cmd/logins/logins.c (revision 0:68f95e015346)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"       /* SVr4.0 1.15.1.2 */
31 
32 /*
33  * logins.c
34  *
35  *	This file contains the source for the administrative command
36  *	"logins" (available to the administrator) that produces a report
37  *	containing login-IDs and other requested information.
38  */
39 
40 /*
41  *  Header files referenced:
42  *	sys/types.h	System data types
43  *	stdio.h		Definitions for standard I/O functions and constants
44  *	unistd.h	Standard UNIX definitions
45  *	string.h	Definitions for string-handling functions
46  *	ctype.h		Character-type definitions
47  *	grp.h		Definitions for referencing the /etc/group file
48  *	pwd.h		Definitions for referencing the /etc/passwd file
49  *	shadow.h	Definitions for the shadow password file /etc/shadow
50  *	time.h		Time definitions (ctime(), asctime(), etc.)
51  *	stdarg.h	Definitions for using a variable argument list
52  *	fmtmsg.h	Definitions for using the standard message generator
53  */
54 
55 #include	<sys/types.h>
56 #include	<stdio.h>
57 #include	<unistd.h>
58 #include	<string.h>
59 #include	<ctype.h>
60 #include	<grp.h>
61 #include	<pwd.h>
62 #include	<shadow.h>
63 #include	<time.h>
64 #include	<stdarg.h>
65 #include	<fmtmsg.h>
66 #include <locale.h>
67 
68 /*
69  *  Externals referenced (and not defined by a header file):
70  *	malloc		Allocate memory from main memory
71  *	getopt		Extract the next option from the command line
72  *	optind		The argument count of the next option to extract
73  *			from the command line
74  *	optarg		A pointer to the argument of the option just extracted
75  *			from the command line
76  *	opterr		FLAG:  !0 tells getopt() to write an error message if
77  *			it detects an error
78  *	getpwent	Get next entry from the /etc/passwd file
79  *	getpwuid	Get next entry from the /etc/passwd file that has a
80  *			specific user-ID
81  *	fgetpwent	Get next entry from an /etc/passwd-like file
82  *	setpwent	Rewind the /etc/passwd file
83  *	endpwent	Quit using the /etc/passwd file
84  *	getgrent	Get next entry from the /etc/group file
85  *	setgrent	Rewind the /etc/group file
86  *	endgrent	Quit using the /etc/passwd file
87  *	getspnam	Get the next entry for a specific login-ID from the
88  *			/etc/shadow file
89  *	setspent	Rewind the /etc/shadow file
90  *	endspent	Quit using the /etc/shadow file
91  *	fmtmsg		Interface to the standard message generation facility
92  *	putenv		Modify the environment
93  *	exit		Exit the program
94  */
95 
96 extern	void	       *malloc();
97 extern	int		getopt();
98 extern	char	       *optarg;
99 extern	int		optind;
100 extern	int		opterr;
101 extern	struct passwd  *getpwent();
102 extern	struct passwd  *getpwuid();
103 extern	struct passwd  *fgetpwent();
104 extern	void		setpwent();
105 extern	void		endpwent();
106 extern	struct group   *getgrent();
107 extern	void		setgrent();
108 extern	void		endgrent();
109 extern	struct spwd    *getspnam();
110 extern	void		setspent();
111 extern	void		endspent();
112 extern	int		fmtmsg();
113 extern	int		putenv();
114 extern	void		exit();
115 
116 /*
117  *  Local constant definitions
118  *	TRUE			Boolean constant
119  *	FALSE			Boolean constant
120  *	USAGE_MSG		Message used to display a usage error
121  *	MAXLOGINSIZE		Maximum length of a valid login-ID
122  *	MAXSYSTEMLOGIN		Maximum value of a system user-ID.
123  *	OPTSTR			Options to this command
124  *	ROOT_ID			The user-ID of an administrator
125  */
126 
127 #ifndef	FALSE
128 #define	FALSE			0
129 #endif
130 
131 #ifndef	TRUE
132 #define	TRUE			((int) 't')
133 #endif
134 
135 #define	USAGE_MSG		"usage: logins [-admopstux] [-g groups] [-l logins]"
136 #define	MAXLOGINSIZE		14
137 #define	MAXSYSTEMLOGIN		99
138 #define	OPTSTR			"adg:l:mopstux"
139 #define	ROOT_ID			0
140 
141 /*
142  *  The following macros do their function for now but will probably have
143  *  to be replaced by functions sometime in the near future.  The maximum
144  *  system login value may someday be administerable, in which case these
145  *  will have to be changed to become functions
146  *
147  *	isasystemlogin	Returns TRUE if the user-ID in the "struct passwd"
148  *			structure referenced by the function's argument is
149  *			less than or equal to the maximum value for a system
150  *			user-ID, FALSE otherwise.
151  *	isauserlogin	Returns TRUE if the user-ID in the "struct passwd"
152  *			structure referenced by the function's argument is
153  *			greater than the maximum value for a system user-ID,
154  *			FALSE otherwise.
155  */
156 
157 #define	isauserlogin(pw)	(pw->pw_uid > MAXSYSTEMLOGIN)
158 #define isasystemlogin(pw)	(pw->pw_uid <= MAXSYSTEMLOGIN)
159 
160 
161 /*
162  *  Local datatype definitions
163  *	struct reqgrp		Describes a group as requested through the
164  *				-g option
165  *	struct reqlogin		Describes a login-ID as requested through
166  *				the -l option
167  *	struct pwdinfo		Describes a password's aging information,
168  *				as extracted from /etc/shadow
169  *	struct secgrp		Describes a login-ID's secondary group
170  */
171 
172 /*  Describes a specified group name (from the -g groups option)  */
173 struct	reqgrp {
174 	char	       *groupname;	/* Requested group name */
175 	struct reqgrp  *next;		/* Next item in the list */
176 	int		found;		/* TRUE if group in /etc/group */
177 	gid_t		groupID;	/* Group's ID */
178 };
179 
180 /*  Describes a specified login name (from the -l logins option)  */
181 struct	reqlogin {
182 	char	        *loginname;	/* Requested login name */
183 	struct reqlogin *next;		/* Next item in the list */
184 	int	 	 found;		/* TRUE if login in /etc/passwd */
185 };
186 
187 /*
188  * This structure describes a password's information
189  */
190 
191 struct	pwdinfo {
192 	long		datechg;	/* Date the password was changed (mmddyy) */
193 	char	       *passwdstatus;	/* Password status */
194 	long		mindaystilchg;	/* Min days b4 pwd can change again */
195 	long		maxdaystilchg;	/* Max days b4 pwd can change again */
196 	long		warninterval;	/* Days before expire to warn user */
197 	long		inactive;	/* Lapsed days of inactivity before lock */
198 	long		expdate;	/* Date of expiration (mmddyy) */
199 };
200 
201 /* This structure describes secondary groups that a user belongs to */
202 struct	secgrp {
203 	char	       *groupname;	/* Name of the group */
204 	struct secgrp  *next;		/* Next item in the list */
205 	gid_t	        groupID;	/* Group-ID */
206 };
207 
208 /*
209  *  These functions handle error and warning message writing.
210  *  (This deals with UNIX(r) standard message generation, so
211  *  the rest of the code doesn't have to.)
212  *
213  *  Functions included:
214  *	initmsg		Initialize the message handling functions.
215  *	wrtmsg		Write the message using fmtmsg().
216  *
217  *  Static data included:
218  *	fcnlbl		The label for standard messages
219  *	msgbuf		A buffer to contain the edited message
220  */
221 
222 static	char	fcnlbl[MM_MXLABELLN+1];	/* Buffer for message label */
223 static	char	msgbuf[MM_MXTXTLN+1];	/* Buffer for message text */
224 
225 /*
226  * void initmsg(p)
227  *
228  *	This function initializes the message handling functions.
229  *
230  *  Arguments:
231  *	p	A pointer to a character string that is the name of the
232  *		function, used to generate the label on messages.  If this
233  *		string contains a slash ('/'), it only uses the characters
234  *		beyond the last slash in the string (this permits argv[0]
235  *		to be used).
236  *
237  *  Returns:  Void
238  */
239 
240 static void
241 initmsg(p)
242 	char   *p;	/* Command name (as invoked) */
243 {
244 	/* Automatic data */
245 	char   *q;	/* Local multi-use pointer */
246 
247 	/* Use only the simple filename if there is a slash in the name */
248 	if (!(q = strrchr(p, '/'))) q = p;
249 	else q++;
250 
251 	/* Build the label for messages */
252 	(void) sprintf(fcnlbl, "UX:%s", q);
253 
254 	/* Restrict messages to the text-component */
255 	(void) putenv("MSGVERB=text");
256 }
257 
258 /*
259  *  void wrtmsg(severity, action, tag, text[, txtarg1[, txtarg2[, ...]]])
260  *
261  *	This function writes a message using fmtmsg()
262  *
263  *  Arguments:
264  *	severity	The severity-component of the message
265  *	action		The action-string used to generate the
266  *			action-component of the message
267  *	tag		Tag-component of the message
268  *	text		The text-string used to generate the text-
269  *			component of the message
270  *	txtarg		Arguments to be inserted into the "text"
271  *			string using vsprintf()
272  *
273  *  Returns:  Void
274  */
275 /*VARARGS4*/
276 static void
277 wrtmsg(int severity, char *action, char *tag, char *text, ...)
278 {
279 	/* Automatic data */
280 	int	errorflg;	/* TRUE if problem generating message */
281 	va_list	argp;		/* Pointer into vararg list */
282 
283 
284 	/* No problems yet */
285 	errorflg = FALSE;
286 
287 	/* Generate the error message */
288 	va_start(argp, text);
289 	if (text != MM_NULLTXT) errorflg = vsprintf(msgbuf, text, argp) > MM_MXTXTLN;
290 	(void) fmtmsg(MM_PRINT, fcnlbl, severity,
291 		      (text == MM_NULLTXT) ? MM_NULLTXT : msgbuf,
292 		      action, tag);
293 	va_end(argp);
294 
295 	/*
296 	 *  If there was a buffer overflow generating the error message,
297 	 *  write a message and quit (things are probably corrupt in the
298 	 *  static data space now
299 	 */
300 	if (errorflg) {
301 	    (void) fmtmsg(MM_PRINT, fcnlbl, MM_WARNING,
302 			  gettext("Internal message buffer overflow"),
303 			  MM_NULLACT, MM_NULLTAG);
304 	    exit(100);
305 	}
306 }
307 /*ARGSUSED*/
308 
309 /*
310  *  These functions allocate space for the information we gather.
311  *  It works by having a memory heap with strings allocated from
312  *  the end of the heap and structures (aligned data) allocated
313  *  from the beginning of the heap.  It begins with a 4k block of
314  *  memory then allocates memory in 4k chunks.  These functions
315  *  should never fail.  If they do, they report the problem and
316  *  exit with an exit code of 101.
317  *
318  *  Functions contained:
319  *	allocblk	Allocates a block of memory, aligned on a
320  *			4-byte (double-word) boundary.
321  *	allocstr	Allocates a block of memory with no
322  *			particular alignment
323  *
324  *  Constant definitions:
325  *	ALLOCBLKSZ	Size of a chunk of main memory allocated
326  *			using malloc()
327  *
328  *  Static data:
329  *	nextblkaddr	Address of the next available chunk of
330  *			aligned space in the heap
331  *	laststraddr	Address of the last chunk of unaligned space
332  *			allocated from the heap
333  *	toomuchspace	Message to write if someone attempts to allocate
334  *			too much space (>ALLOCBLKSZ bytes)
335  *	memallocdif	Message to write if there is a problem
336  *			allocating main menory.
337  */
338 
339 #define	ALLOCBLKSZ	4096
340 
341 static	char   *nextblkaddr = (char *) NULL;
342 static	char   *laststraddr = (char *) NULL;
343 #define	MEMALLOCDIF	"Memory allocation difficulty.  Command terminates"
344 #define	TOOMUCHSPACE	"Internal space allocation error.  Command terminates"
345 
346 /*
347  *  void *allocblk(size)
348  *	unsigned int	size
349  *
350  *	This function allocates a block of aligned (4-byte or
351  *	double-word boundary) memory from the program's heap.
352  *	It returns a pointer to that block of allocated memory.
353  *
354  *  Arguments:
355  *	size		Minimum number of bytes to allocate (will
356  *			round up to multiple of 4)
357  *
358  *  Returns:  void *
359  *	Pointer to the allocated block of memory
360  */
361 
362 static void *
363 allocblk(size)
364 	unsigned int	size;
365 {
366 	/* Automatic data */
367 	char   *rtnval;
368 
369 
370 	/* Make sure the sizes are aligned correctly */
371 	if ((size = size + (4 - (size % 4))) > ALLOCBLKSZ) {
372 	    wrtmsg(MM_ERROR, MM_NULLACT, MM_NULLTAG, gettext(TOOMUCHSPACE));
373 	    exit(101);
374 	}
375 
376 	/* Set up the value we're going to return */
377 	rtnval = nextblkaddr;
378 
379 	/* Get the space we need off of the heap */
380 	if ((nextblkaddr += size) >= laststraddr) {
381 	    if (!(rtnval = (char *) malloc(ALLOCBLKSZ))) {
382 		wrtmsg(MM_ERROR, MM_NULLACT, MM_NULLTAG, gettext(MEMALLOCDIF));
383 		exit(101);
384 	    }
385 	    laststraddr = rtnval + ALLOCBLKSZ;
386 	    nextblkaddr = rtnval + size;
387 	}
388 
389 	/* We're through */
390 	return((void *) rtnval);
391 }
392 
393 /*
394  *  char *allocstr(nbytes)
395  *	unsigned int	nbytes
396  *
397  *	This function allocates a block of unaligned memory from the
398  *	program's heap.  It returns a pointer to that block of allocated
399  *	memory.
400  *
401  *  Arguments:
402  *	nbytes		Number of bytes to allocate
403  *
404  *  Returns:  char *
405  *	Pointer to the allocated block of memory
406  */
407 
408 static char *
409 allocstr(nchars)
410 	unsigned int	nchars;
411 {
412 	if (nchars > ALLOCBLKSZ) {
413 	    wrtmsg(MM_ERROR, MM_NULLACT, MM_NULLTAG, gettext(TOOMUCHSPACE));
414 	    exit(101);
415 	}
416 	if (laststraddr == NULL ||
417 	    (laststraddr -= nchars) < nextblkaddr) {
418 	    if (!(nextblkaddr = (char *) malloc(ALLOCBLKSZ))) {
419 		wrtmsg(MM_ERROR, MM_NULLACT, MM_NULLTAG, gettext(MEMALLOCDIF));
420 		exit(101);
421 	    }
422 	    laststraddr = nextblkaddr + ALLOCBLKSZ - nchars;
423 	}
424 	return(laststraddr);
425 }
426 
427 /*
428  *  These functions control the group membership list, as found in
429  *  the /etc/group file.
430  *
431  *  Functions included:
432  *	initmembers		Initialize the membership list (to NULL)
433  *	addmember		Adds a member to the membership list
434  *	isamember		Looks for a particular login-ID in the
435  *				list of members
436  *
437  *  Datatype Definitions:
438  *	struct grpmember	Describes a group member
439  *
440  *  Static Data:
441  *	membershead		Pointer to the head of the list of
442  *				group members
443  */
444 
445 struct	grpmember {
446 	char	               *membername;
447 	struct grpmember       *next;
448 };
449 
450 static	struct grpmember       *membershead;
451 
452 /*
453  *  void initmembers()
454  *
455  *	This function initializes the list of members of specified groups.
456  *
457  *  Arguments:  None
458  *
459  *  Returns:  Void
460  */
461 
462 static void
463 initmembers()
464 {
465 	/* Set up the members list to be a null member's list */
466 	membershead = (struct grpmember *) NULL;
467 }
468 
469 /*
470  *  void addmember(p)
471  *	char   *p
472  *
473  *	This function adds a member to the group member's list.  The
474  *	group members list is a list of structures containing a pointer
475  *	to the member-name and a pointer to the next item in the
476  *	structure.  The structure is not ordered in any particular way.
477  *
478  *  Arguments:
479  *	p	Pointer to the member name
480  *
481  *  Returns:  Void
482  */
483 
484 static void
485 addmember(p)
486 	char   *p;
487 {
488 	/* Automatic data */
489 	struct grpmember       *new;	/* Member being added */
490 
491 	new = (struct grpmember *) allocblk(sizeof(struct grpmember));
492 	new->membername = strcpy(allocstr((unsigned int) strlen(p)+1), p);
493 	new->next = membershead;
494 	membershead = new;
495 }
496 
497 /*
498  *  init isamember(p)
499  *	char   *p
500  *
501  *	This function examines the list of group-members for the string
502  *	referenced by 'p'.  If 'p' is a member of the members list, the
503  *	function returns TRUE.  Otherwise it returns FALSE.
504  *
505  *  Arguments:
506  *	p	Pointer to the name to search for.
507  *
508  *  Returns:  int
509  *	TRUE	If 'p' is found in the members list,
510  *	FALSE	otherwise
511  */
512 
513 static int
514 isamember(p)
515 	char   *p;
516 {
517 	/* Automatic Data */
518 	int			found;	/* TRUE if login found in list */
519 	struct grpmember       *pmem;	/* Group member being examined */
520 
521 
522 	/* Search the membership list for 'p' */
523 	found = FALSE;
524 	for (pmem = membershead ; !found && pmem ; pmem = pmem->next) {
525 	    if (strcmp(p, pmem->membername) == 0) found = TRUE;
526 	}
527 
528 	return (found);
529 }
530 
531 /*
532  *  These functions handle the display list.  The display list contains
533  *  all of the information we're to display.  The list contains a pointer
534  *  to the login-name, a pointer to the free-field (comment), and a
535  *  pointer to the next item in the list.  The list is ordered alpha-
536  *  betically (ascending) on the login-name field.  The list initially
537  *  contains a dummy field (to make insertion easier) that contains a
538  *  login-name of "".
539  *
540  *  Functions included:
541  *	initdisp	Initializes the display list
542  *	adddisp		Adds information to the display list
543  *	isuidindisp	Looks to see if a particular user-ID is in the
544  *			display list
545  *	genreport	Generates a report from the items in the display
546  *			list
547  *	applygroup	Add group information to the items in the display
548  *			list
549  *	applypasswd	Add extended password information to the items
550  *			in the display list
551  *
552  *  Datatypes Defined:
553  *	struct display	Describes the structure that contains the information
554  *			to be displayed.  Includes pointers to the login-ID,
555  *			free-field (comment), and the next structure in the
556  *			list.
557  *
558  *  Static Data:
559  *	displayhead	Pointer to the head of the display list.  Initially
560  *			references the null-item on the head of the list.
561  */
562 
563 struct	display {
564 	char	       *loginID;	/* Login name */
565 	char	       *freefield;	/* Free (comment) field */
566 	char	       *groupname;	/* Name of the primary group */
567 	char	       *iwd;		/* Initial working directory */
568 	char	       *shell;		/* Shell after login (may be null) */
569 	struct pwdinfo *passwdinfo;	/* Password information structure */
570 	struct secgrp  *secgrplist; 	/* Head of the secondary group list */
571 	uid_t		userID;		/* User ID */
572 	gid_t		groupID;	/* Group ID of primary group */
573 	struct display *nextlogin;	/* Next login in the list */
574 	struct display *nextuid;	/* Next user-ID in the list */
575 };
576 
577 static	struct display *displayhead;
578 
579 /*
580  *  void initdisp()
581  *
582  *	Initializes the display list.  An empty display list contains
583  *	a single element, the dummy element.
584  *
585  *  Arguments:  None
586  *
587  *  Returns:  Void
588  */
589 
590 static void
591 initdisp()
592 {
593 	displayhead = (struct display *) allocblk(sizeof(struct display));
594 	displayhead->nextlogin = (struct display *) NULL;
595 	displayhead->nextuid = (struct display *) NULL;
596 	displayhead->loginID = "";
597 	displayhead->freefield = "";
598 	displayhead->userID = -1;
599 }
600 
601 /*
602  *  void adddisp(pwent)
603  *	struct passwd  *pwent
604  *
605  *	This function adds the appropriate information from the login
606  *	description referenced by 'pwent' to the list if information
607  *	to be displayed.  It only adds the information if the login-ID
608  *	(user-name) is unique.  It inserts the information in the list
609  *	in such a way that the list remains ordered alphabetically
610  *	(ascending) according to the login-ID (user-name).
611  *
612  *  Arguments:
613  *	pwent		Structure that contains all of the login information
614  *			of the login being added to the list.  The only
615  *			information that this function uses is the login-ID
616  *			(user-name) and the free-field (comment field).
617  *
618  *  Returns:  Void
619  */
620 
621 static void
622 adddisp(pwent)
623 	struct passwd  *pwent;
624 {
625 	/* Automatic data */
626 	struct display *new;		/* Item being added to the list */
627 	struct display *prev;		/* Previous item in the list */
628 	struct display *current;	/* Next item in the list */
629 	int		found;		/* FLAG, insertion point found */
630 	int		compare = 1;	/* strcmp() compare value */
631 
632 
633 	/* Find where this value belongs in the list */
634 	prev = displayhead;
635 	found = FALSE;
636 	while (!found && (current = prev->nextlogin))
637 	    if ((compare = strcmp(current->loginID, pwent->pw_name)) >= 0) found = TRUE;
638 	    else prev = current;
639 
640 	/* Insert this value in the list, only if it is unique though */
641 	if (compare != 0) {
642 	    new = (struct display *) allocblk(sizeof(struct display));
643 	    new->loginID = strcpy(allocstr((unsigned int) strlen(pwent->pw_name)+1), pwent->pw_name);
644 	    if (pwent->pw_comment && pwent->pw_comment[0] != '\0')
645 		    new->freefield =
646 			    strcpy(allocstr((unsigned int)
647 					    strlen(pwent->pw_comment)+1),
648 				   pwent->pw_comment);
649 	    else
650 		    new->freefield =
651 			    strcpy(allocstr((unsigned int)
652 					    strlen(pwent->pw_gecos)+1),
653 				   pwent->pw_gecos);
654 	    if (!pwent->pw_shell || !(*pwent->pw_shell)) new->shell = "/sbin/sh";
655 	    else new->shell = strcpy(allocstr((unsigned int) strlen(pwent->pw_shell)+1), pwent->pw_shell);
656 	    new->iwd = strcpy(allocstr((unsigned int) strlen(pwent->pw_dir)+1), pwent->pw_dir);
657 	    new->userID = pwent->pw_uid;
658 	    new->groupID = pwent->pw_gid;
659 	    new->secgrplist = (struct secgrp *) NULL;
660 	    new->passwdinfo = (struct pwdinfo *) NULL;
661 	    new->groupname = (char *) NULL;
662 
663 	    /* Add new display item to the list ordered by login-ID */
664 	    new->nextlogin = current;
665 	    prev->nextlogin = new;
666 
667 	    /* Find the appropriate place for the new item in the list
668 	     * ordered by userID */
669 	    prev = displayhead;
670 	    found = FALSE;
671 	    while (!found && (current = prev->nextuid))
672 		if (current->userID > pwent->pw_uid) found = TRUE;
673 		else prev = current;
674 
675 	    /* Add the item into the list that is ordered by user-ID */
676 	    new->nextuid = current;
677 	    prev->nextuid = new;
678 	}
679 }
680 
681 /*
682  *  int isuidindisp(pwent)
683  *	struct passwd  *pwent
684  *
685  *  This function examines the display list to see if the uid in
686  *  the (struct passwd) referenced by "pwent" is already in the
687  *  display list.  It returns TRUE if it is in the list, FALSE
688  *  otherwise.
689  *
690  *  Since the display list is ordered by user-ID, the search continues
691  *  until a match is found or a user-ID is found that is larger than
692  *  the one we're searching for.
693  *
694  *  Arguments:
695  *	pwent		Structure that contains the user-ID we're to
696  *			look for
697  *
698  *  Returns:  int
699  *	TRUE if the user-ID was found, FALSE otherwise.
700  */
701 
702 static int
703 isuidindisp(pwent)
704 	struct passwd  *pwent;		/* Struct of user to look for */
705 {
706 	/* Automatic data */
707 	struct display *dp;
708 
709 
710 	/*
711 	 *  Search the list, beginning at the beginning (where else?)
712 	 *  and stopping when the user-ID is found or one is found that
713 	 *  is greater than the user-ID we're searching for.  Recall
714 	 *  that this list is ordered by user-ID
715 	 */
716 
717 	for (dp = displayhead->nextuid ; dp && (dp->userID < pwent->pw_uid) ; dp = dp->nextuid) ;
718 
719 	/* If the pointer "dp" points to a structure that has a
720 	 * matching user-ID, return TRUE.  Otherwise FALSE */
721 	return (dp && (dp->userID == pwent->pw_uid));
722 }
723 
724 /*
725  *  void applygroup(allgroups)
726  *	int	allgroups
727  *
728  *  This function applies group information to the login-IDs in the
729  *  display list.  It always applies the primary group information.
730  *  If "allgroups" is TRUE, it applies secondary information as well.
731  *
732  *  Arguments:
733  * 	allgroups	FLAG.  TRUE if secondary group info is to be
734  *			applied -- FALSE otherwise.
735  *
736  *  Returns:  void
737  */
738 
739 static void
740 applygroup(allgroups)
741 	int	allgroups;	/* TRUE if applying secondary groups */
742 {
743 	/* Automatic Data */
744 	struct display *dp;		/* Display list running ptr */
745 	struct group   *grent;		/* Group info, from getgrent() */
746 	char	       *p;		/* Temp char pointer */
747 	char          **pp;		/* Temp char * pointer */
748 	int		compare;	/* Value from strcmp() */
749 	int		done;		/* TRUE if finished, FALSE otherwise */
750 	struct secgrp  *psecgrp;	/* Block allocated for this info */
751 	struct secgrp  *psrch;		/* Secondary group -- for searching */
752 
753 
754 	/* For each group-ID in the /etc/group file ... */
755 	while (grent = getgrent()) {
756 	    /*
757 	     *  Set the primary group for the login-IDs in the display
758 	     *  list.  For each group-ID we get, leaf through the display
759 	     *  list and set the group-name if the group-IDs match
760 	     */
761 
762 	    p = (char *) NULL;
763 	    for (dp = displayhead->nextuid ; dp ; dp = dp->nextuid)
764 		if ((dp->groupID == grent->gr_gid) && !dp->groupname) {
765 		    if (!p) p = strcpy(allocstr((unsigned int) strlen(grent->gr_name)+1), grent->gr_name);
766 		    dp->groupname = p;
767 		}
768 
769 	    /*
770 	     *  If we're to be displaying secondary group membership,
771 	     *  leaf through the list of group members.  Then, attempt
772 	     *  to find that member in the display list.  When found,
773 	     *  attach secondary group info to the user.
774 	     */
775 
776 	    if (allgroups) for (pp = grent->gr_mem ; *pp ; pp++) {
777 		done = FALSE;
778 		for (dp = displayhead->nextlogin ; !done && dp ; dp = dp->nextlogin) {
779 		    if (((compare = strcmp(dp->loginID, *pp)) == 0) && !(grent->gr_gid == dp->groupID)) {
780 			if (!p) p = strcpy(allocstr((unsigned int) strlen(grent->gr_name)+1), grent->gr_name);
781 			psecgrp = (struct secgrp *) allocblk(sizeof(struct secgrp));
782 			psecgrp->groupID = grent->gr_gid;
783 			psecgrp->groupname = p;
784 			psecgrp->next = (struct secgrp *) NULL;
785 			if (!dp->secgrplist) dp->secgrplist = psecgrp;
786 			else {
787 			    for (psrch = dp->secgrplist ; psrch->next ; psrch = psrch->next) ;
788 			    psrch->next = psecgrp;
789 			}
790 			done = TRUE;
791 		    }
792 		    else if (compare > 0) done = TRUE;
793 		}
794 	    }
795 	}
796 
797 	/* Close the /etc/group file */
798 	endgrent();
799 }
800 
801 /*
802  *  void applypasswd()
803  *
804  *	This function applies extended password information to an item
805  *	to be displayed.  It allocates space for a structure describing
806  *	the password, then fills in that structure from the information
807  *	in the /etc/shadow file.
808  *
809  *  Arguments:  None
810  *
811  *  Returns:  Void
812  */
813 
814 static void
815 applypasswd()
816 {
817 	/*
818 	 *  Local declarations
819 	 */
820 
821 	struct pwdinfo *ppasswd;	/* Ptr to pwd desc in current element */
822 	struct display *dp;		/* Ptr to current element */
823 	struct spwd    *psp;		/* Pointer to a shadow-file entry */
824 	struct tm      *ptm;		/* Pointer to a time-of-day structure */
825 	char	       *p;		/* Running character pointer */
826 	time_t 		pwchg;		/* System-time of last pwd chg */
827 	time_t 		pwexp;		/* System-time of password expiration */
828 
829 
830 	/*  Make sure the shadow file is rewound  */
831 	setspent();
832 
833 
834 	/*
835 	 *  For each item in the display list...
836 	 */
837 
838 	for (dp = displayhead->nextuid ; dp ; dp = dp->nextuid) {
839 
840 	    /* Allocate structure space for the password description */
841 	    ppasswd = (struct pwdinfo *) allocblk(sizeof(struct pwdinfo));
842 	    dp->passwdinfo = ppasswd;
843 
844 	    /*
845 	     * If there's no entry in the /etc/shadow file, assume
846 	     * that the login is locked
847 	     */
848 
849 	    if (!(psp = getspnam(dp->loginID))) {
850 		pwchg = 0L;			/* Epoch */
851 		ppasswd->passwdstatus = "LK";	/* LK, Locked */
852 		ppasswd->mindaystilchg = 0L;
853 		ppasswd->maxdaystilchg = 0L;
854 		ppasswd->warninterval = 0L;
855 		ppasswd->inactive = 0L;
856 		pwexp = 0L;
857 	    }
858 
859 	    /*
860 	     * Otherwise, fill in the password information from the
861 	     * info in the shadow file entry
862 	     */
863 
864 	    else {
865 		/*  See if the login has no password  */
866 		if (!psp->sp_pwdp || !(*psp->sp_pwdp)) ppasswd->passwdstatus = "NP";
867 
868 		/*
869 		 * See if the login is explicitly locked (encrypted
870 		 * password is <13 characters)
871 		 */
872 
873 		else if (strlen(psp->sp_pwdp) != 13) ppasswd->passwdstatus = "LK";
874 
875 		/*
876 		 * If it's a valid encrypted password, the login is
877 		 * password protected
878 		 */
879 		else {
880 		    ppasswd->passwdstatus = "PS";
881 		    for (p = psp->sp_pwdp ; *p ; p++) {
882 			if (!isalnum(*p) && (*p != '.') && (*p != '/')) {
883 			    ppasswd->passwdstatus = "LK";
884 			    break;
885 			}
886 		    }
887 		}
888 
889 		/*
890 		 * Set up the last-changed date, the minimum days between
891 		 * changes, the maximum life of a password, the interval
892 		 * before expiration that the user is warned, the number of
893 		 * days a login can be inactive before it expires, and the
894 		 * login expiration date
895 		 */
896 
897 		pwchg = psp->sp_lstchg;
898 		ppasswd->mindaystilchg = psp->sp_min;
899 		ppasswd->maxdaystilchg = psp->sp_max;
900 		ppasswd->warninterval = psp->sp_warn;
901 		ppasswd->inactive = psp->sp_inact;
902 		pwexp = psp->sp_expire;
903 	    }
904 
905 	    /*
906 	     * Convert the date of the last password change from days-
907 	     * since-epoch to mmddyy (integer) form.  Involves the
908 	     * intermediate step of converting the date from days-
909 	     * since-epoch to seconds-since-epoch.  We'll set this to
910 	     * somewhere near the middle of the day, since there are not
911 	     * always 24*60*60 seconds in a year.  (Yeech)
912 	     *
913 	     * Note:  The form mmddyy should probably be subject to
914 	     * internationalization -- Non-Americans will think that
915 	     * 070888 is 07 August 88 when the software is trying to say
916 	     * 08 July 88.  Systems Engineers seem to think that this isn't
917 	     * a problem though...
918 	     */
919 
920 	    if (pwchg != -1L) {
921 	        pwchg = (pwchg * DAY) + (DAY/2);
922 		ptm = localtime(&pwchg);
923 		ppasswd->datechg = ((long) (ptm->tm_mon+1) * 10000L) +
924 				    (long) ((ptm->tm_mday * 100) +
925 					    (ptm->tm_year % 100));
926 	    } else ppasswd->datechg = 0L;
927 
928 	    /*
929 	     * Convert the passwd expiration date from days-since-epoch
930 	     * to mmddyy (long integer) form using the same algorithm and
931 	     * comments as above.
932 	     */
933 
934 	    if (pwexp != -1L) {
935 		pwexp = (pwexp * DAY) + (DAY/2);
936 		ptm = localtime(&pwexp);
937 		ppasswd->expdate = ((long) (ptm->tm_mon+1) * 10000L) +
938 				    (long) ((ptm->tm_mday * 100) +
939 					    (ptm->tm_year % 100));
940 	    } else ppasswd->expdate = 0L;
941 	}
942 
943 	/* Close the shadow password file */
944 	endspent();
945 }
946 
947 /*
948  * int hasnopasswd(pwent)
949  *	struct passwd  *pwent
950  *
951  *	This function examines a login's password-file entry
952  *	and, if necessary, its shadow password-file entry and
953  *	returns TRUE if that user-ID has no password, meaning
954  *	that the user-ID can be used to log into the system
955  *	without giving a password.  The function returns FALSE
956  *	otherwise.
957  *
958  *  Arguments:
959  *	pwent	Login to examine.
960  *
961  *  Returns:  int
962  *	TRUE if the login can be used without a password, FALSE
963  *	otherwise.
964  */
965 
966 static int
967 hasnopasswd(pwent)
968 	struct passwd  *pwent;	/* /etc/passwd entry of login to check */
969 {
970 	/* Local definitions */
971 	struct spwd    *psp;		/* /etc/shadow file struct */
972 	int		nopwflag;	/* TRUE if login has no passwd */
973 
974 	/*
975 	 *  A login has no password if:
976 	 *    1.  There exists an entry for that login in the
977 	 *	  shadow password-file (/etc/passwd), and
978 	 *    2.  The encrypted password in the structure describing
979 	 *	  that entry is either:
980 	 *	      a.  (char *) NULL
981 	 *	      b.  A null string ("")
982 	 */
983 
984 	/* Get the login's entry in the shadow password file */
985 	if (psp = getspnam(pwent->pw_name)) {
986 
987 	    /* Look at the encrypted password in that entry */
988 	    if (psp->sp_pwdp == (char *)0 ||
989 			 *psp->sp_pwdp == '\0')
990 		nopwflag = TRUE;
991 	    else
992 		nopwflag = FALSE;
993 	}
994 	else
995 		nopwflag = FALSE;
996 
997 	/* Done ... */
998 	return(nopwflag);
999 }
1000 
1001 /*
1002  *  void writeunformatted(current, xtndflag, expflag)
1003  *	struct display *current
1004  *	int		xtndflag
1005  *	int		expflag
1006  *
1007  *  This function writes the data in the display structure "current"
1008  *  to the standard output file.  It writes the information in the
1009  *  form of a colon-list.  It writes secondary group information if
1010  *  that information is in the structure, it writes extended
1011  *  (initial working directory, shell, and password-aging) info
1012  *  if the "xtndflag" is TRUE, and it writes password expiration
1013  *  information if "expflag" is TRUE.
1014  *
1015  *  Arguments:
1016  *	current		Structure containing information to write.
1017  *	xtndflag	TRUE if extended information is to be written,
1018  *			FALSE otherwise
1019  *	expflag		TRUE if password expiration information is to
1020  *			be written, FALSE otherwise
1021  *
1022  *  Returns:  void
1023  */
1024 
1025 static void
1026 writeunformatted(current, xtndflag, expflag)
1027 	struct display *current;	/* Struct with info to write */
1028 	int		xtndflag;	/* Write extended output flag */
1029 	int		expflag;	/* Write password expiration info flag */
1030 {
1031 	/* Automatic data */
1032 	struct secgrp  *psecgrp;	/* Secondary group info */
1033 	struct pwdinfo *pwdinfo;	/* Password aging info */
1034 
1035 	/* Write the general information */
1036 	(void) fprintf(stdout, "%s:%ld:%s:%ld:%s",
1037 		       current->loginID,
1038 		       current->userID,
1039 		       current->groupname == (char *) NULL ? "" : current->groupname,
1040 		       current->groupID,
1041 		       current->freefield);
1042 
1043 	/*
1044 	 * If the group information is there, write it (it's only
1045 	 * there if it's supposed to be written)
1046 	 */
1047 	for (psecgrp = current->secgrplist ; psecgrp ; psecgrp = psecgrp->next)
1048 	    (void) fprintf(stdout, ":%s:%ld", psecgrp->groupname, psecgrp->groupID);
1049 
1050 	/* If the extended info flag is TRUE, write the extended information */
1051 	if (xtndflag) {
1052 	    pwdinfo = current->passwdinfo;
1053 	    (void) fprintf(stdout, ":%s:%s:%s:%6.6ld:%ld:%ld:%ld",
1054 			   current->iwd, current->shell,
1055 			   pwdinfo->passwdstatus,
1056 			   pwdinfo->datechg,
1057 			   pwdinfo->mindaystilchg, pwdinfo->maxdaystilchg,
1058 			   pwdinfo->warninterval);
1059 	}
1060 
1061 	/* If the password expiration information is requested, write it.  */
1062 	if (expflag) {
1063 	    pwdinfo = current->passwdinfo;
1064 	    (void) fprintf(stdout, ":%ld:%ld", pwdinfo->inactive, pwdinfo->expdate);
1065 	}
1066 
1067 	/* Terminate the information with a new-line */
1068 	(void) putc('\n', stdout);
1069 }
1070 
1071 /*
1072  *  void writeformatted(current, xtndflag, expflag)
1073  *	struct display *current
1074  *	int		xtndflag
1075  *	int		expflag
1076  *
1077  *  This function writes the data in the display structure "current"
1078  *  to the standard output file.  It writes the information in an
1079  *  easily readable format.  It writes secondary group information
1080  *  if that information is in the structure, it writes extended
1081  *  (initial working directory, shell, and password-aging) info if
1082  *  "xtndflag" is TRUE, and it write password expiration information
1083  *  if "expflag" is TRUE.
1084  *
1085  *  Arguments:
1086  *	current		Structure containing info to write.
1087  *	xtndflag	TRUE if extended information is to be written,
1088  *			FALSE otherwise
1089  *	expflag		TRUE if password expiration information is to be written,
1090  *			FALSE otherwise
1091  *
1092  *  Returns:  void
1093  */
1094 
1095 static void
1096 writeformatted(current, xtndflag, expflag)
1097 	struct display *current;	/* Struct with info to write */
1098 	int		xtndflag;	/* Write extended output flag */
1099 	int		expflag;	/* Write password expiration info flag */
1100 {
1101 	/* Automatic data */
1102 	struct secgrp  *psecgrp;	/* Secondary group info */
1103 	struct pwdinfo *pwdinfo;	/* Password aging info */
1104 
1105 	/* Write general information */
1106 	(void) fprintf(stdout, "%-14s  %-6ld  %-14s  %-6ld  %s\n",
1107 		       current->loginID, current->userID,
1108 		       current->groupname == (char *) NULL ? "" : current->groupname,
1109 		       current->groupID, current->freefield);
1110 
1111 	/*
1112 	 * Write information about secondary groups if the info exists
1113 	 * (it only exists if it is to be written)
1114 	 */
1115 	for (psecgrp = current->secgrplist ; psecgrp ; psecgrp = psecgrp->next)
1116 	    (void) fprintf(stdout, "                        %-14s  %-6ld\n",
1117 			   psecgrp->groupname, psecgrp->groupID);
1118 
1119 	/* If the extended information flag is TRUE, write the extended information */
1120 
1121 	if (xtndflag) {
1122 	    pwdinfo = current->passwdinfo;
1123 	    (void) fprintf(stdout, "                        %s\n", current->iwd);
1124 	    (void) fprintf(stdout, "                        %s\n", current->shell);
1125 	    (void) fprintf(stdout, "                        %s %6.6ld %ld %ld %ld\n",
1126 			   pwdinfo->passwdstatus,
1127 			   pwdinfo->datechg, pwdinfo->mindaystilchg,
1128 			   pwdinfo->maxdaystilchg,
1129 			   pwdinfo->warninterval);
1130 	}
1131 
1132 	/* If the password expiration info flag is TRUE, write that information */
1133 	if (expflag) {
1134 	    pwdinfo = current->passwdinfo;
1135 	    (void) fprintf(stdout, "                        %ld %6.6ld\n",
1136 			   pwdinfo->inactive, pwdinfo->expdate);
1137     }
1138 }
1139 
1140 /*
1141  *  void genuidreport(pipeflag, xtndflag, expflag)
1142  *	int	pipeflag
1143  *	int	xtndflag
1144  *	int	expflag
1145  *
1146  *	This function generates a report on the standard output
1147  *	stream (stdout) containing the login-IDs in the list of
1148  *	logins built by this command.  The list is ordered based
1149  *	on user-ID.  If the <pipeflag> variable is not zero, it
1150  *	will generate a report containing parsable records.
1151  *	Otherwise, it will generate a columnarized report.  If
1152  *	the <xtndflag> variable is not zero, it will include the
1153  *	extended set of information (password aging info, home
1154  *	directory, shell process, etc.).  If <expflag> is not
1155  *	zero, it will display password expiration information.
1156  *
1157  *  Arguments:
1158  *	pipeflag	int
1159  *			TRUE if a parsable report is needed,
1160  *			FALSE if a columnar report is needed
1161  *	xtndflag	int
1162  *			TRUE if extended set of info is to be displayed,
1163  *			FALSE otherwise
1164  *	expflag		int
1165  *			TRUE if password expiration information is to be
1166  *			displayed, FALSE otherwise
1167  *
1168  *  Returns:  void
1169  */
1170 
1171 static void
1172 genuidreport(pipeflag, xtndflag, expflag)
1173 	int	pipeflag;	/* Parsible output flag */
1174 	int	xtndflag;	/* Extended info flag */
1175 	int	expflag;	/* Password expiration info flag */
1176 {
1177 
1178 	/* Automatic data */
1179 	struct display *current;	/* Data being displayed */
1180 
1181 
1182 	/*
1183 	 *  Initialization for loop.
1184 	 *  (NOTE:  The first element in the list of logins to
1185 	 *          display is a dummy element.)
1186 	 */
1187 	current = displayhead;
1188 
1189 	/*
1190 	 *  Display elements in the list
1191 	 */
1192 	if (pipeflag)
1193 	    for (current = displayhead->nextuid ; current ; current = current->nextuid)
1194 	    	writeunformatted(current, xtndflag, expflag);
1195 	else
1196 	    for (current = displayhead->nextuid ; current ; current = current->nextuid)
1197 	    	writeformatted(current, xtndflag, expflag);
1198 }
1199 
1200 /*
1201  *  void genlogreport(pipeflag, xtndflag, expflag)
1202  *	int	pipeflag
1203  *	int	xtndflag
1204  *	int	expflag
1205  *
1206  *	This function generates a report on the standard output
1207  *	stream (stdout) containing the login-IDs in the list of
1208  *	logins built by this command.  The list is ordered based
1209  *	on user name.  If the <pipeflag> variable is not zero, it
1210  *	will generate a report containing parsable records.
1211  *	Otherwise, it will generate a columnarized report.  If
1212  *	the <xtndflag> variable is not zero, it will include the
1213  *	extended set of information (password aging info, home
1214  *	directory, shell process, etc.).  If <expflag> is not
1215  *	zero, it will include password expiration information.
1216  *
1217  *  Arguments:
1218  *	pipeflag	int
1219  *			TRUE if a parsable report is needed,
1220  *			FALSE if a columnar report is needed
1221  *	xtndflag	int
1222  *			TRUE if extended set of info is to be displayed,
1223  *			FALSE otherwise
1224  *	expflag		int
1225  *			TRUE if password expiration information is to
1226  *			be displayed, FALSE otherwise
1227  *
1228  *  Returns:  void
1229  */
1230 
1231 static void
1232 genlogreport(pipeflag, xtndflag, expflag)
1233 	int	pipeflag;	/* Parsable output flag */
1234 	int	xtndflag;	/* Extended info flag */
1235 	int	expflag;	/* Password expiration info flag */
1236 {
1237 
1238 	/* Automatic data */
1239 	struct display *p;	/* Value being displayed */
1240 
1241 
1242 	/*
1243 	 *  Initialization for loop.
1244 	 *  (NOTE:  The first element in the list of logins to
1245 	 *          display is a dummy element.)
1246 	 */
1247 	p = displayhead;
1248 
1249 	/*
1250 	 *  Display elements in the list
1251 	 */
1252 	if (pipeflag)
1253 	    for (p = displayhead->nextlogin ; p ; p = p->nextlogin)
1254 	    	writeunformatted(p, xtndflag, expflag);
1255 	else
1256 	    for (p = displayhead->nextlogin ; p ; p = p->nextlogin)
1257 	    	writeformatted(p, xtndflag, expflag);
1258 }
1259 
1260 char *
1261 strcpmalloc(str)
1262 char *str;
1263 {
1264 	char *cp;
1265 
1266 	if (str == NULL)
1267 		return NULL;
1268 
1269 	return (strcpy(allocstr((unsigned int)strlen(str)+1), str));
1270 }
1271 
1272 struct localpw {
1273 	struct localpw *next;
1274 	struct passwd pw;
1275 };
1276 
1277 struct localpw *pwtable = NULL;
1278 
1279 struct localpw *pwptr;		/* Local passwd pointer for getpwent()
1280 				 * -- -1 means not in use, NULL for EOF */
1281 
1282 int in_localgetpwent = 0;	/* Set if in local_getpwent */
1283 
1284 void
1285 build_localpw()
1286 {
1287 	struct localpw *next, *cur;
1288 	struct passwd *pw;
1289 
1290 	next = (struct localpw *) allocblk(sizeof (struct localpw));
1291 
1292 	pwtable = next;
1293 
1294 	while ((pw = getpwent()) != NULL) {
1295 		/*
1296 		 * Copy the data -- we have to alloc areas for it all
1297 		 */
1298 		next->pw.pw_name = strcpmalloc(pw->pw_name);
1299 		next->pw.pw_passwd = strcpmalloc(pw->pw_passwd);
1300 		next->pw.pw_uid = pw->pw_uid;
1301 		next->pw.pw_gid = pw->pw_gid;
1302 		next->pw.pw_age = strcpmalloc(pw->pw_age);
1303 		next->pw.pw_comment = strcpmalloc(pw->pw_comment);
1304 		next->pw.pw_gecos  = strcpmalloc(pw->pw_gecos);
1305 		next->pw.pw_dir = strcpmalloc(pw->pw_dir);
1306 		next->pw.pw_shell = strcpmalloc(pw->pw_shell);
1307 
1308 		next->next = (struct localpw *) allocblk(sizeof (struct localpw));
1309 
1310 		cur = next;
1311 		next = next->next;
1312 	}
1313 
1314 	/*
1315 	 * At this point we have one extra (struct localpw) allocated;
1316 	 * sine alloclbk doesn't have a freeblk, we just leave it unreferenced.
1317 	 */
1318 
1319 	if (pwtable == next)
1320 		pwtable = NULL;
1321 	else
1322 		cur->next = NULL;
1323 
1324 	endpwent();
1325 }
1326 
1327 struct passwd *
1328 local_getpwent()
1329 {
1330 	if (!in_localgetpwent) {
1331 		in_localgetpwent = 1;
1332 		pwptr = pwtable;
1333 	} else if ( pwptr != NULL)
1334 		pwptr = pwptr->next;
1335 
1336 	if (pwptr != NULL)
1337 		return &(pwptr->pw);
1338 	else
1339 		return NULL;
1340 }
1341 
1342 void
1343 local_endpwent()
1344 {
1345 	in_localgetpwent = 0;
1346 }
1347 
1348 long
1349 local_pwtell()
1350 {
1351 	return (long)pwptr;
1352 }
1353 
1354 void
1355 local_pwseek(ptr)
1356 long ptr;
1357 {
1358 	pwptr = (struct localpw *)ptr;
1359 }
1360 
1361 /*
1362  * logins [-admopstux] [-l logins] [-g groups]
1363  *
1364  *	This command generates a report of logins administered on
1365  *	the system.  The list will contain logins that meet criteria
1366  *	described by the options in the list.  If there are no options,
1367  *	it will list all logins administered.  It is intended to be used
1368  *	only by administrators.
1369  *
1370  *  Options:
1371  *	-a		Display password expiration information.
1372  *	-d		list all logins that share user-IDs with another
1373  *			login.
1374  *	-g groups	specifies the names of the groups to which a login
1375  *			must belong before it is included in the generated
1376  *			list.  "groups" is a comma-list of group names.
1377  *	-l logins	specifies the logins to display.  "logins" is a
1378  *			comma-list of login names.
1379  *	-m		in addition to the usual information, for each
1380  *			login displayed, list all groups to which that
1381  *			login is member.
1382  *	-o		generate a report as a colon-list instead of in a
1383  *			columnar format
1384  *	-p		list all logins that have no password.
1385  *	-s		list all system logins
1386  *	-t		sort the report lexicographically by login name
1387  *			instead of by user-ID
1388  *	-u		list all user logins
1389  *	-x		in addition to the usual information, display an
1390  *			extended set of information that includes the home
1391  *			directory, initial process, and password status and
1392  *			aging information
1393  *
1394  * Exit Codes:
1395  *	0	All's well that ends well
1396  *	1	Usage error
1397  */
1398 
1399 main(argc, argv)
1400 	int	argc;	/* Number of args on the command line */
1401 	char   *argv[];	/* Pointers pointing to the arguments */
1402 {
1403 
1404 	/* Automatic data */
1405 
1406 	struct passwd	       *plookpwd;	/* Ptr to searcher pw (-d) */
1407 	struct reqgrp	       *reqgrphead;	/* Head of the req'd group list */
1408 	struct reqgrp	       *pgrp;		/* Current item in req'd group list */
1409 	struct reqgrp	       *qgrp;		/* Prev item in the req'd group list */
1410 	struct reqlogin        *reqloginhead;	/* Head of req'd login list */
1411 	struct reqlogin	       *plogin;		/* Current item in the req'd login list */
1412 	struct reqlogin	       *qlogin;		/* Prev item in the req'd login list */
1413 	struct passwd	       *pwent;		/* /etc/passwd entry */
1414 	struct group	       *grent;		/* /etc/group entry */
1415 	char		       *token;		/* Token extracted by strtok() */
1416 	char		      **pp;		/* Group member */
1417 	char		       *g_arg;		/* -g option's argument */
1418 	char		       *l_arg;		/* -l option's argument */
1419 	long			lookpos;	/* File pos'n, rec we're looking for */
1420 	int			a_seen;		/* Is -a requested? */
1421 	int			d_seen;		/* Is -d requested? */
1422 	int	 	 	g_seen;		/* Is -g requested? */
1423 	int			l_seen;		/* Is -l requested? */
1424 	int			m_seen;		/* Is -m requested? */
1425 	int			o_seen;		/* Is -o requested? */
1426 	int			p_seen;		/* Is -p requested? */
1427 	int			s_seen;		/* Is -s requested? */
1428 	int			t_seen;		/* Is -t requested? */
1429 	int			u_seen;		/* Is -u requested? */
1430 	int			x_seen;		/* Is -x requested? */
1431 	int			errflg;		/* Is there a command-line problem */
1432 	int			done;		/* Is the process (?) is complete */
1433 	int			groupcount;	/* Number of groups specified by the user */
1434 	int			doall;		/* Are all logins to be reported */
1435 	int			c;		/* Character returned from getopt() */
1436 
1437 	(void) setlocale(LC_ALL, "");
1438 
1439 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
1440 #define TEXT_DOMAIN "SYS_TEST"
1441 #endif
1442 	(void) textdomain(TEXT_DOMAIN);
1443 
1444 	/* Initializations */
1445 	initmsg(argv[0]);
1446 
1447 
1448 
1449 	/*
1450 	 *  Command-line processing
1451 	 */
1452 
1453 	/* Initializations */
1454 	a_seen = FALSE;
1455 	d_seen = FALSE;
1456 	g_seen = FALSE;
1457 	l_seen = FALSE;
1458 	m_seen = FALSE;
1459 	o_seen = FALSE;
1460 	p_seen = FALSE;
1461 	s_seen = FALSE;
1462 	t_seen = FALSE;
1463 	u_seen = FALSE;
1464 	x_seen = FALSE;
1465 	errflg = FALSE;
1466 	opterr = 0;
1467 	while (!errflg && ((c = getopt(argc, argv, OPTSTR)) != EOF)) {
1468 
1469 	    /* Case on the option character */
1470 	    switch(c) {
1471 
1472 	    /*
1473 	     * -a option:
1474 	     * Display password expiration information
1475 	     */
1476 
1477 	    case 'a':
1478 		if (a_seen) errflg = TRUE;
1479 		else a_seen = TRUE;
1480 		break;
1481 
1482 	    /*
1483 	     * -d option:
1484 	     * Display logins which share user-IDs with other logins
1485 	     */
1486 
1487 	    case 'd':
1488 		if (d_seen) errflg = TRUE;
1489 		else d_seen = TRUE;
1490 		break;
1491 
1492 	    /*
1493 	     * -g <groups> option:
1494 	     * Display the specified groups
1495 	     */
1496 
1497 	    case 'g':
1498 		if (g_seen) errflg = TRUE;
1499 		else {
1500 		    g_seen = TRUE;
1501 		    g_arg = optarg;
1502 		}
1503 		break;
1504 
1505 	    /*
1506 	     * -l <logins> option:
1507 	     * Display the specified logins
1508 	     */
1509 
1510 	    case 'l':
1511 		if (l_seen) errflg = TRUE;
1512 		else {
1513 		    l_seen = TRUE;
1514 		    l_arg = optarg;
1515 		}
1516 		break;
1517 
1518 	    /*
1519 	     * -m option:
1520 	     * Display multiple group information
1521 	     */
1522 
1523 	    case 'm':
1524 		if (m_seen) errflg = TRUE;
1525 		else m_seen = TRUE;
1526 		break;
1527 
1528 	    /*
1529 	     * -o option:
1530 	     * Display information as a colon-list
1531 	     */
1532 
1533 	    case 'o':
1534 		if (o_seen) errflg = TRUE;
1535 		else o_seen = TRUE;
1536 		break;
1537 
1538 	    /*
1539 	     * -p option:
1540 	     * Select logins that have no password
1541 	     */
1542 
1543 	    case 'p':
1544 		if (p_seen) errflg = TRUE;
1545 		else p_seen = TRUE;
1546 		break;
1547 
1548 	    /*
1549 	     * -s option:
1550 	     * Select system logins
1551 	     */
1552 
1553 	    case 's':
1554 		if (s_seen) errflg = TRUE;
1555 		else s_seen = TRUE;
1556 		break;
1557 
1558 	    /*
1559 	     * -t option:
1560 	     * Sort alphabetically by login-ID instead of numerically
1561 	     * by user-ID
1562 	     */
1563 
1564 	    case 't':
1565 		if (t_seen) errflg = TRUE;
1566 		else t_seen = TRUE;
1567 		break;
1568 
1569 	    /*
1570 	     * -u option:
1571 	     * Select user logins
1572 	     */
1573 
1574 	    case 'u':
1575 		if (u_seen) errflg = TRUE;
1576 		else u_seen = TRUE;
1577 		break;
1578 
1579 	    /*
1580 	     * -x option:
1581 	     * Display extended info (init working dir, shell, pwd info)
1582 	     */
1583 
1584 	    case 'x':
1585 		if (x_seen) errflg = TRUE;
1586 		else x_seen = TRUE;
1587 		break;
1588 
1589 	    default:			/* Oops.... */
1590 		errflg = TRUE;
1591 	    }
1592 	}
1593 
1594 	/* Write out a usage message if necessary and quit */
1595 	if (errflg || (optind != argc)) {
1596 	    wrtmsg(MM_ERROR, MM_NULLACT, MM_NULLTAG, gettext(USAGE_MSG));
1597 	    exit(1);
1598 	}
1599 
1600 
1601 
1602 	/*
1603 	 *  The following section does preparation work, setting up for
1604 	 *  building the list of logins to display
1605 	 */
1606 
1607 	/*
1608 	 * Very first thing, build an in-core structure of passwd file entries.
1609 	 * This is important since we have to assume that getpwent() is going
1610 	 * out to one or more network name services that could be changing
1611 	 * on the fly.  This will limit us to one pass through the network data.
1612 	 */
1613 	build_localpw();
1614 
1615 
1616 	/*
1617 	 *  If the -g groups option was on the command line, build a
1618 	 *  list containing groups we're to list logins for.
1619 	 */
1620 
1621 	if (g_seen) {
1622 	    groupcount = 0;
1623 	    reqgrphead = (struct reqgrp *) NULL;
1624 	    if (token = strtok(g_arg, ",")) {
1625 		pgrp = (struct reqgrp *) allocblk(sizeof(struct reqgrp));
1626 		pgrp->groupname = token;
1627 		pgrp->found = FALSE;
1628 		pgrp->next = (struct reqgrp *) NULL;
1629 		groupcount++;
1630 		reqgrphead = pgrp;
1631 		qgrp = pgrp;
1632 		while (token = strtok(NULL, ",")) {
1633 		    pgrp = (struct reqgrp *) allocblk(sizeof(struct reqgrp));
1634 		    pgrp->groupname = token;
1635 		    pgrp->found = FALSE;
1636 		    pgrp->next = (struct reqgrp *) NULL;
1637 		    groupcount++;
1638 		    qgrp->next = pgrp;
1639 		    qgrp = pgrp;
1640 		}
1641 	    }
1642 	}
1643 
1644 
1645 	/*
1646 	 *  If -l logins is on the command line, build a list of
1647 	 *  logins we're to generate reports for.
1648 	 */
1649 
1650 	if (l_seen) {
1651 	    reqloginhead = (struct reqlogin *) NULL;
1652 	    if (token = strtok(l_arg, ",")) {
1653 		plogin = (struct reqlogin *) allocblk(sizeof(struct reqlogin));
1654 		plogin->loginname = token;
1655 		plogin->found = FALSE;
1656 		plogin->next = (struct reqlogin *) NULL;
1657 		reqloginhead = plogin;
1658 		qlogin = plogin;
1659 		while (token = strtok(NULL, ",")) {
1660 		    plogin = (struct reqlogin *) allocblk(sizeof(struct reqlogin));
1661 		    plogin->loginname = token;
1662 		    plogin->found = FALSE;
1663 		    plogin->next = (struct reqlogin *) NULL;
1664 		    qlogin->next = plogin;
1665 		    qlogin = plogin;
1666 		}
1667 	    }
1668 	}
1669 
1670 	if (l_seen) {
1671 		while(pwent = local_getpwent()) {
1672 			done = FALSE;
1673 			for (plogin = reqloginhead ; !done && plogin ;
1674 						plogin = plogin->next) {
1675 				if (strcmp(pwent->pw_name,
1676 					   plogin->loginname) == 0) {
1677 				    	plogin->found = TRUE;
1678 			    		done = TRUE;
1679 				}
1680 		    	}
1681 		}
1682 		local_endpwent();
1683 	}
1684 
1685 	/*
1686 	 *  Generate the list of login information to display
1687 	 */
1688 
1689 	/* Initialize the login list */
1690 	initmembers();
1691 
1692 
1693 	/*
1694 	 *  If -g groups was specified, generate a list of members
1695 	 *  of the specified groups
1696 	 */
1697 
1698 	if (g_seen) {
1699 
1700 	    /* For each group in the /etc/group file ... */
1701 	    while (grent = getgrent()) {
1702 
1703 		/* For each group mentioned with the -g option ... */
1704 		for (pgrp = reqgrphead ; (groupcount > 0) && pgrp ; pgrp = pgrp->next) {
1705 
1706 		    if (!pgrp->found) {
1707 
1708 			/*
1709 			 *  If the mentioned group is found in the
1710 			 *  /etc/group file ...
1711 			 */
1712 			if (strcmp(grent->gr_name, pgrp->groupname) == 0) {
1713 
1714 			    /*
1715 			     * Mark the entry is found, remembering the
1716 			     * group-ID for later
1717 			     */
1718 
1719 			    pgrp->found = TRUE;
1720 			    groupcount--;
1721 			    pgrp->groupID = grent->gr_gid;
1722 			    for (pp = grent->gr_mem ; *pp ; pp++) addmember(*pp);
1723 			}
1724 		    }
1725 		}
1726 	    }
1727 
1728 
1729 	    /*
1730 	     * If any groups weren't found, write a message indicating
1731 	     * such, then continue
1732 	     */
1733 
1734 	    qgrp = (struct reqgrp *) NULL;
1735 	    for (pgrp = reqgrphead ; pgrp ; pgrp = pgrp->next) {
1736 		if (!pgrp->found) {
1737 		    wrtmsg(MM_WARNING, MM_NULLACT, MM_NULLTAG, gettext("%s was not found"), pgrp->groupname);
1738 		    if (!qgrp) reqgrphead = pgrp->next;
1739 		    else qgrp->next = pgrp->next;
1740 		}
1741 		else qgrp = pgrp;
1742 	    }
1743 	    endgrent();
1744 	}
1745 
1746 
1747 	/* Initialize the list of logins to display */
1748 	initdisp();
1749 
1750 
1751 	/*
1752 	 *  Add logins that have user-IDs that are used more than once,
1753 	 *  if requested.  This command is pretty slow, since the algorithm
1754 	 *  reads from the /etc/passwd file 1+2+3+...+n times where n is the
1755 	 *  number of login-IDs in the /etc/passwd file.  (Actually, this
1756 	 *  can be optimized so it's not quite that bad, but the order or
1757 	 *  magnitude stays the same.)
1758 	 *
1759 	 *  Note:  This processing needs to be done before any other options
1760 	 *         are processed -- the algorithm contains an optimization
1761 	 *	   that insists on the display list being empty before this
1762 	 *	   option is processed.
1763 	 */
1764 
1765 	if (d_seen) {
1766 
1767 	    /*
1768 	     * The following code is a quick&dirty reimplementation of the
1769 	     * original algorithm, which opened the password file twice (to
1770 	     * get two file pointer into the data) and then used fgetpwent()
1771 	     * in undocumented ways to scan through the file, checking for
1772 	     * duplicates.  This does not work when getpwent() is used to
1773 	     * go out over the network, since there is not file pointer.
1774 	     *
1775 	     * Instead an in-memory list of passwd structures is built, and then
1776 	     * this list is scanned.  The routines Local_getpwent(), etc.,
1777 	     * are designed to mimic the standard library routines, so this code
1778 	     * does not have to be extensively modified.
1779 	     */
1780 
1781 	    /*
1782 	     * For reference, here is the original comment about the next
1783 	     * section of code.  Some of the code has changed, but the algorithm
1784 	     * is the same:
1785 	     *
1786 	     * Open the system password file once.  This instance will be
1787 	     * used to leaf through the file once, reading each entry once,
1788 	     * and searching the remainder of the file for another login-ID
1789 	     * that has the same user-ID.  Note that there are lots of
1790 	     * contortions one has to go through when reading two instances
1791 	     * of the /etc/passwd file.  That's why there's some seeking,
1792 	     * re-reading of the same record, and other junk.  Luckily, this
1793 	     * feature won't be requested very often, and still isn't too
1794 	     * slow...
1795 	     */
1796 
1797 	    /* For each entry in the passwd database ... */
1798 	    while (plookpwd = local_getpwent()) {
1799 		/*
1800 		 *  Optimization -- If the login's user-ID is already in
1801 		 *  the display list, there's no reason to process this
1802 		 *  entry -- it's already there.
1803 		 */
1804  		if (!isuidindisp(plookpwd)) {
1805 
1806 		    /*
1807 		     * Rememeber the current entry's position, so when we finish
1808 		     * scanning through the database looking for duplicates
1809 		     * we can return to the current place, so that the enclosing
1810 		     * loop will march in an orderly fashion through the passwd
1811 		     * database.
1812 		     */
1813 		    done = FALSE;
1814 		    lookpos = local_pwtell();
1815 
1816 		    /*
1817 		     * For each record in the passwd database beyond
1818 		     * the searching record ...
1819 		     */
1820 		    while (pwent = local_getpwent()) {
1821 
1822 			/*
1823 			 * If there's a match between the searcher's user-
1824 			 * ID and the searchee's user-ID ...
1825 			 */
1826 			if (pwent->pw_uid == plookpwd->pw_uid) {
1827 
1828 			    /*
1829 			     * If this is the first duplicate of this searcher
1830 			     * that we find,
1831 			     * add the searcher's record to the display list
1832 			     * (It wants to be on the list first
1833 			     * to avoid ordering "flakeyness")
1834 			     */
1835 			    if (done == FALSE) {
1836 				adddisp(plookpwd);
1837 				done == TRUE;
1838 			    }
1839 
1840 			    /*
1841 			     * Now add the searchee's record
1842 			     */
1843 			    adddisp(pwent);
1844 
1845 		    	}
1846 		    }
1847 		    /* Reposition to searcher record */
1848 		    local_pwseek(lookpos);
1849 		}
1850 	    }
1851 
1852 	    local_endpwent();
1853 	}
1854 
1855 
1856 	/*
1857 	 *  Loop through the passwd database squirelling away the
1858 	 *  information we need for the display.
1859 	 *
1860 	 *  NOTE:  Once a login is added to the list, the rest of the
1861 	 *	   body of the loop is bypassed (via a continue statement).
1862 	 */
1863 
1864 	doall = !(s_seen || u_seen || p_seen || d_seen || l_seen || g_seen);
1865 
1866 	if (doall || s_seen || u_seen || p_seen || l_seen || g_seen) {
1867 
1868 	    while (pwent = local_getpwent()) {
1869 		done = FALSE;
1870 
1871 		/* If no user-specific options were specified,
1872 		 * include this login-ID */
1873 		if (doall) {
1874 		    adddisp(pwent);
1875 		    continue;
1876 		}
1877 
1878 		/* If the user specified system login-IDs,
1879 		 * and this is a system ID, include it */
1880 		if (s_seen) if (isasystemlogin(pwent)) {
1881 		    adddisp(pwent);
1882 		    continue;
1883 		}
1884 
1885 		/* If the user specified user login-IDs,
1886 		 * and this is a user ID, include it */
1887 		if (u_seen) if (isauserlogin(pwent)) {
1888 		    adddisp(pwent);
1889 		    continue;
1890 		}
1891 
1892 		/* If the user is asking for login-IDs that have
1893 		 * no password, and this one has no password,
1894 		 * include it */
1895 		if (p_seen) if (hasnopasswd(pwent)) {
1896 		    adddisp(pwent);
1897 		    continue;
1898 		}
1899 
1900 		/*
1901 		 * If specific logins were requested, leaf through
1902 		 * the list of logins they requested.  If this login
1903 		 * is on the list, include it.
1904 		 */
1905 		if (l_seen) {
1906 		    for (plogin = reqloginhead ; !done && plogin ; plogin = plogin->next) {
1907 			if (strcmp(pwent->pw_name, plogin->loginname) == 0) {
1908 			    plogin->found = TRUE;
1909 			    adddisp(pwent);
1910 			    done = TRUE;
1911 			}
1912 		    }
1913 		    if (done) continue;
1914 		}
1915 
1916 		/*
1917 		 * If specific groups were requested, leaf through the
1918 		 * list of login-IDs that belong to those groups.  If this
1919 		 * login-ID is in that list, or its primary group is one
1920 		 * of those requested, include it.
1921 		 */
1922 
1923 		if (g_seen) {
1924 		    for (pgrp = reqgrphead ; !done && pgrp ; pgrp = pgrp->next)
1925 			if (pwent->pw_gid == pgrp->groupID) {
1926 			    adddisp(pwent);
1927 			    done = TRUE;
1928 		    }
1929 		    if (!done && isamember(pwent->pw_name)) {
1930 			adddisp(pwent);
1931 			done = TRUE;
1932 		    }
1933 		}
1934 		if (done) continue;
1935 	    }
1936 	    local_endpwent();
1937 	}
1938 
1939 	/* Let the user know about logins they requested that
1940 	 * don't exist */
1941 	if (l_seen) for (plogin = reqloginhead ; plogin ; plogin = plogin->next)
1942 	    if (!plogin->found)
1943 		wrtmsg(MM_WARNING, MM_NULLACT, MM_NULLTAG, gettext("%s was not found"), plogin->loginname);
1944 
1945 	/*
1946 	 *  Apply group information
1947 	 */
1948 	applygroup(m_seen);
1949 
1950 
1951 	/*
1952 	 * Apply password information (only needed if the extended
1953 	 * set of information has been requested)
1954 	 */
1955 	if (x_seen || a_seen) applypasswd();
1956 
1957 
1958 	/*
1959 	 * Generate a report from this display items we've squirreled
1960 	 * away
1961 	 */
1962 
1963 	if (t_seen) genlogreport(o_seen, x_seen, a_seen);
1964 	else genuidreport(o_seen, x_seen, a_seen);
1965 
1966 	/*
1967 	 *  We're through!
1968 	 */
1969 	exit(0);
1970 
1971 #ifdef	lint
1972 	return(0);
1973 #endif
1974 }
1975