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