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 /*
24 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
26 */
27
28 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
29 /* All Rights Reserved */
30
31 #pragma ident "%Z%%M% %I% %E% SMI"
32
33 /*
34 * users.c
35 *
36 * This file contains the source for the administrative command
37 * "listusers" (available to the general user population) that
38 * produces a report containing user login-IDs and their "free
39 * field" (typically contains the user's name and other information).
40 */
41
42 /*
43 * Header files referenced:
44 * sys/types.h System type definitions
45 * stdio.h Definitions for standard I/O functions and constants
46 * string.h Definitions for string-handling functions
47 * grp.h Definitions for referencing the /etc/group file
48 * pwd.h Definitions for referencing the /etc/passwd file
49 * varargs.h Definitions for using a variable argument list
50 * fmtmsg.h Definitions for using the standard message formatting
51 * facility
52 */
53
54 #include <sys/types.h>
55 #include <stdio.h>
56 #include <string.h>
57 #include <grp.h>
58 #include <pwd.h>
59 #include <stdarg.h>
60 #include <fmtmsg.h>
61 #include <stdlib.h>
62
63
64 /*
65 * Externals referenced (and not defined by a header file):
66 * malloc Allocate memory from main memory
67 * getopt Extract the next option from the command line
68 * optind The argument count of the next option to extract from
69 * the command line
70 * optarg A pointer to the argument of the option just extracted
71 * from the command line
72 * opterr FLAG: !0 tells getopt() to write an error message if
73 * it detects an error
74 * getpwent Get next entry from the /etc/passwd file
75 * getgrent Get next entry from the /etc/group file
76 * fmtmsg Standard message generation facility
77 * putenv Modify the environment
78 * exit Exit the program
79 */
80
81 /*
82 * Local constant definitions
83 */
84
85 #ifndef FALSE
86 #define FALSE 0
87 #endif
88
89 #ifndef TRUE
90 #define TRUE ('t')
91 #endif
92
93 #define USAGE_MSG "usage: listusers [-g groups] [-l logins]"
94 #define MAXLOGINSIZE 14
95 #define LOGINFIELDSZ MAXLOGINSIZE+2
96
97 #define isauserlogin(uid) (uid >= 100)
98 #define isasystemlogin(uid) (uid < 100)
99 #define isausergroup(gid) (gid >= 100)
100 #define isasystemgroup(gid) (gid < 100)
101
102 /*
103 * Local datatype definitions
104 */
105
106 /*
107 * This structure describes a specified group name
108 * (from the -g groups option)
109 */
110
111 struct reqgrp {
112 char *groupname;
113 struct reqgrp *next;
114 int found;
115 gid_t groupID;
116 };
117
118 /*
119 * This structure describes a specified login name
120 * (from the -l logins option)
121 */
122
123 struct reqlogin {
124 char *loginname;
125 struct reqlogin *next;
126 int found;
127 };
128
129 /*
130 * These functions handle error and warning message writing.
131 * (This deals with UNIX(r) standard message generation, so
132 * the rest of the code doesn't have to.)
133 *
134 * Functions included:
135 * initmsg Initialize the message handling functions.
136 * wrtmsg Write the message using the standard message
137 * generation facility.
138 *
139 * Static data included:
140 * fcnlbl The label for standard messages
141 * msgbuf A buffer to contain the edited message
142 */
143
144 static char fcnlbl[MM_MXLABELLN+1]; /* Buffer for message label */
145 static char msgbuf[MM_MXTXTLN+1]; /* Buffer for message text */
146
147 /*
148 * void initmsg(p)
149 *
150 * This function initializes the message handling functions.
151 *
152 * Arguments:
153 * p A pointer to a character string that is the name of the
154 * command, used to generate the label on messages. If this
155 * string contains a slash ('/'), it only uses the characters
156 * beyond the last slash in the string (this permits argv[0]
157 * to be used).
158 *
159 * Returns: Void
160 */
161
162 static void
initmsg(char * p)163 initmsg(char *p) /* Ptr to command name */
164 {
165 /* Automatic data */
166 char *q; /* Local multi-use pointer */
167
168 /* Use only the simple filename if there is a slash in the name */
169 if ((q = strrchr(p, '/')) == NULL)
170 q = p;
171 else
172 q++;
173
174 /* Build the label for messages */
175 (void) snprintf(fcnlbl, sizeof (fcnlbl), "UX:%s", q);
176
177 /*
178 * Now that we've done all of that work, set things up so that
179 * only the text-component of a message is printed. (This piece
180 * of code will most probably go away in SVR4.1.
181 */
182 (void) putenv("MSGVERB=text");
183 }
184
185 /*
186 * void wrtmsg(severity, action, tag, text[, txtarg1[, txtarg2[, ...]]])
187 *
188 * This function writes a message using the standard message
189 * generation facility.
190 *
191 * Arguments:
192 * severity The severity-component of the message
193 * action The action-string used to generate the action-
194 * component of the message
195 * tag Tag-component of the message
196 * text The text-string used to generate the text-component
197 * of the message
198 * txtarg Arguments to be inserted into the "text" string using
199 * vsnprintf()
200 *
201 * Returns: Void
202 */
203
204 /* VARARGS4 */
205
206 static void
wrtmsg(int severity,char * action,char * tag,char * text,...)207 wrtmsg(int severity, char *action, char *tag, char *text, ...)
208 {
209 /* Automatic data */
210 int errorflg; /* FLAG: True if error writing msg */
211 va_list argp; /* Pointer into vararg list */
212
213 errorflg = FALSE;
214
215 /* Generate the error message */
216 va_start(argp, text);
217 if (text != MM_NULLTXT) {
218 /* LINTED */
219 errorflg = vsnprintf(msgbuf, sizeof (msgbuf), text, argp) >
220 MM_MXTXTLN;
221 }
222 (void) fmtmsg(MM_PRINT, fcnlbl, severity,
223 (text == MM_NULLTXT) ? MM_NULLTXT : msgbuf,
224 action, tag);
225 va_end(argp);
226
227 /*
228 * If there would have been a buffer overflow generating the error
229 * message, the message will be truncated, so write a message and quit.
230 */
231
232 if (errorflg) {
233 (void) fmtmsg(MM_PRINT, fcnlbl, MM_WARNING,
234 "Internal message buffer overflow",
235 MM_NULLACT, MM_NULLTAG);
236 exit(100);
237 }
238 }
239
240 /*
241 * These functions allocate space for the information we gather.
242 * It works by having a memory heap with strings allocated from
243 * the end of the heap and structures (aligned data) allocated
244 * from the beginning of the heap. It begins with a 4k block of
245 * memory then allocates memory in 4k chunks. These functions
246 * should never fail. If they do, they report the problem and
247 * exit with an exit code of 101.
248 *
249 * Functions contained:
250 * allocblk Allocates a block of memory, aligned on a
251 * 4-byte (double-word) boundary.
252 * allocstr Allocates a block of memory with no particular
253 * alignment
254 *
255 * Constant definitions:
256 * ALLOCBLKSZ Size of a chunk of main memory allocated using
257 * malloc()
258 *
259 * Static data:
260 * nextblkaddr Address of the next available chunk of aligned
261 * space in the heap
262 * laststraddr Address of the last chunk of unaligned space
263 * allocated from the heap
264 * toomuchspace Message to write if someone attempts to allocate
265 * too much space (>ALLOCBLKSZ bytes)
266 * memallocdif Message to write if there is a problem allocating
267 * main memory.
268 */
269
270 #define ALLOCBLKSZ 4096
271
272 static char *nextblkaddr = NULL;
273 static char *laststraddr = NULL;
274 static char *memallocdif =
275 "Memory allocation difficulty. Command terminates";
276 static char *toomuchspace =
277 "Internal space allocation error. Command terminates";
278
279 /*
280 * void *allocblk(size)
281 * unsigned int size
282 *
283 * This function allocates a block of aligned (4-byte or double-
284 * word boundary) memory from the program's heap. It returns a
285 * pointer to that block of allocated memory.
286 *
287 * Arguments:
288 * size Minimum number of bytes to allocate (will
289 * round up to multiple of 4)
290 *
291 * Returns: void *
292 * Pointer to the allocated block of memory
293 */
294
295 static void *
allocblk(unsigned int size)296 allocblk(unsigned int size)
297 {
298 /* Automatic data */
299 char *rtnval;
300
301
302 /* Make sure the sizes are aligned correctly */
303 if ((size = size + (4 - (size % 4))) > ALLOCBLKSZ) {
304 wrtmsg(MM_ERROR, MM_NULLACT, MM_NULLTAG, toomuchspace);
305 exit(101);
306 }
307
308 /* Set up the value we're going to return */
309 rtnval = nextblkaddr;
310
311 /* Get the space we need off of the heap */
312 if ((nextblkaddr += size) >= laststraddr) {
313 if ((rtnval = malloc(ALLOCBLKSZ)) == NULL) {
314 wrtmsg(MM_ERROR, MM_NULLACT, MM_NULLTAG, memallocdif);
315 exit(101);
316 }
317 laststraddr = rtnval + ALLOCBLKSZ;
318 nextblkaddr = rtnval + size;
319 }
320
321 /* We're through */
322 return ((void *)rtnval);
323 }
324
325 /*
326 * char *allocstr(nbytes)
327 * unsigned int nbytes
328 *
329 * This function allocates a block of unaligned memory from the
330 * program's heap. It returns a pointer to that block of allocated
331 * memory.
332 *
333 * Arguments:
334 * nbytes Number of bytes to allocate
335 *
336 * Returns: char *
337 * Pointer to the allocated block of memory
338 */
339
340 static char *
allocstr(unsigned int nchars)341 allocstr(unsigned int nchars)
342 {
343 if (nchars > ALLOCBLKSZ) {
344 wrtmsg(MM_ERROR, MM_NULLACT, MM_NULLTAG, toomuchspace);
345 exit(101);
346 }
347 if ((laststraddr -= nchars) < nextblkaddr) {
348 if ((nextblkaddr = malloc(ALLOCBLKSZ)) == NULL) {
349 wrtmsg(MM_ERROR, MM_NULLACT, MM_NULLTAG, memallocdif);
350 exit(101);
351 }
352 laststraddr = nextblkaddr + ALLOCBLKSZ - nchars;
353 }
354 return (laststraddr);
355 }
356
357 /*
358 * These functions control the group membership list, as found in the
359 * /etc/group file.
360 *
361 * Functions included:
362 * initmembers Initialize the membership list (to NULL)
363 * addmember Adds a member to the membership list
364 * isamember Looks for a particular login-ID in the list
365 * of members
366 *
367 * Datatype Definitions:
368 * struct grpmember Describes a group member
369 *
370 * Static Data:
371 * membershead Pointer to the head of the list of group members
372 */
373
374 struct grpmember {
375 char *membername;
376 struct grpmember *next;
377 };
378
379 static struct grpmember *membershead;
380
381 /*
382 * void initmembers()
383 *
384 * This function initializes the list of members of specified groups.
385 *
386 * Arguments: None
387 *
388 * Returns: Void
389 */
390
391 static void
initmembers(void)392 initmembers(void)
393 {
394 /* Set up the members list to be a null member's list */
395 membershead = NULL;
396 }
397
398 /*
399 * void addmember(p)
400 * char *p
401 *
402 * This function adds a member to the group member's list. The
403 * group members list is a list of structures containing a pointer
404 * to the member-name and a pointer to the next item in the structure.
405 * The structure is not ordered in any particular way.
406 *
407 * Arguments:
408 * p Pointer to the member name
409 *
410 * Returns: Void
411 */
412
413 static void
addmember(char * p)414 addmember(char *p)
415 {
416 /* Automatic data */
417 struct grpmember *new; /* Member being added */
418
419 new = (struct grpmember *)allocblk(sizeof (struct grpmember));
420 new->membername = strcpy(allocstr((unsigned int)strlen(p)+1), p);
421 new->next = membershead;
422 membershead = new;
423 }
424
425 /*
426 * init isamember(p)
427 * char *p
428 *
429 * This function examines the list of group-members for the string
430 * referenced by 'p'. If 'p' is a member of the members list, the
431 * function returns TRUE. Otherwise it returns FALSE.
432 *
433 * Arguments:
434 * p Pointer to the name to search for.
435 *
436 * Returns: int
437 * TRUE If 'p' is found in the members list,
438 * FALSE otherwise
439 */
440
441 static int
isamember(char * p)442 isamember(char *p)
443 {
444 /* Automatic Data */
445 int found; /* FLAG: TRUE if login found */
446 struct grpmember *pmem; /* Pointer to group member */
447
448
449 /* Search the membership list for the 'p' */
450 found = FALSE;
451 for (pmem = membershead; !found && pmem; pmem = pmem->next) {
452 if (strcmp(p, pmem->membername) == 0) found = TRUE;
453 }
454
455 return (found);
456 }
457
458 /*
459 * These functions handle the display list. The display list contains
460 * all of the information we're to display. The list contains a pointer
461 * to the login-name, a pointer to the free-field (comment), and a pointer
462 * to the next item in the list. The list is ordered alphabetically
463 * (ascending) on the login-name field. The list initially contains a
464 * dummy field (to make insertion easier) that contains a login-name of "".
465 *
466 * Functions included:
467 * initdisp Initializes the display list
468 * adddisp Adds information to the display list
469 * genreport Generates a report from the items in the display list
470 *
471 * Datatypes Defined:
472 * struct display Describes the structure that contains the
473 * information to be displayed. Includes pointers
474 * to the login-ID, free-field (comment), and the
475 * next structure in the list.
476 *
477 * Static Data:
478 * displayhead Pointer to the head of the list of login-IDs to
479 * be displayed. Initially references the null-item
480 * on the head of the list.
481 */
482
483 struct display {
484 char *loginID;
485 char *freefield;
486 struct display *next;
487 };
488
489 static struct display *displayhead;
490
491 /*
492 * void initdisp()
493 *
494 * Initializes the display list. An empty display list contains a
495 * single element, the dummy element.
496 *
497 * Arguments: None
498 *
499 * Returns: Void
500 */
501
502 static void
initdisp(void)503 initdisp(void)
504 {
505 displayhead = (struct display *)allocblk(sizeof (struct display));
506 displayhead->next = NULL;
507 displayhead->loginID = "";
508 displayhead->freefield = "";
509 }
510
511 /*
512 * void adddisp(pwent)
513 * struct passwd *pwent
514 *
515 * This function adds the appropriate information from the login
516 * description referenced by 'pwent' to the list if information
517 * to be displayed. It only adds the information if the login-ID
518 * (user-name) is unique. It inserts the information in the list
519 * in such a way that the list remains ordered alphabetically
520 * (ascending) according to the login-ID (user-name).
521 *
522 * Arguments:
523 * pwent Points to the (struct passwd) structure that
524 * contains all of the login information on the
525 * login being added to the list. The only
526 * information that this function uses is the
527 * login-ID (user-name) and the free-field
528 * (comment field).
529 *
530 * Returns: Void
531 */
532
533 static void
adddisp(struct passwd * pwent)534 adddisp(struct passwd *pwent)
535 {
536 /* Automatic data */
537 struct display *new; /* Display item being added */
538 struct display *prev; /* Previous display item */
539 struct display *current; /* Next display item */
540 int found; /* FLAG, insertion point found */
541 int compare = 1; /* strcmp() compare value */
542
543
544 /* Find where this value belongs in the list */
545 prev = displayhead;
546 current = displayhead->next;
547 found = FALSE;
548 while (!found && current) {
549 if ((compare = strcmp(current->loginID, pwent->pw_name)) >= 0)
550 found = TRUE;
551 else {
552 prev = current;
553 current = current->next;
554 }
555 }
556
557 /* Insert this value in the list, only if it is unique though */
558 if (compare != 0) {
559 /*
560 * Build a display structure containing the value to add to
561 * the list, and add to the list
562 */
563 new = (struct display *)allocblk(sizeof (struct display));
564 new->loginID =
565 strcpy(allocstr((unsigned int)strlen(pwent->pw_name)+1),
566 pwent->pw_name);
567 if (pwent->pw_comment && pwent->pw_comment[0] != '\0')
568 new->freefield =
569 strcpy(allocstr(
570 (unsigned int)strlen(pwent->pw_comment)+1),
571 pwent->pw_comment);
572 else
573 new->freefield =
574 strcpy(allocstr(
575 (unsigned int)strlen(pwent->pw_gecos)+1),
576 pwent->pw_gecos);
577 new->next = current;
578 prev->next = new;
579 }
580 }
581
582 /*
583 * void genreport()
584 *
585 * This function generates a report on the standard output stream
586 * (stdout) containing the login-IDs and the free-fields of the
587 * logins that match the list criteria (-g and -l options)
588 *
589 * Arguments: None
590 *
591 * Returns: Void
592 */
593
594 static void
genreport(void)595 genreport(void)
596 {
597
598 /* Automatic data */
599 struct display *current; /* Value to display */
600 int i; /* Counter of characters */
601
602 /*
603 * Initialization for loop.
604 * (NOTE: The first element in the list of logins to display
605 * is a dummy element.)
606 */
607 current = displayhead;
608
609 /*
610 * Display elements in the list
611 */
612 for (current = displayhead->next; current; current = current->next) {
613 (void) fputs(current->loginID, stdout);
614 for (i = LOGINFIELDSZ - strlen(current->loginID); --i >= 0;
615 (void) putc(' ', stdout))
616 ;
617 (void) fputs(current->freefield, stdout);
618 (void) putc('\n', stdout);
619 }
620 }
621
622 /*
623 * listusers [-l logins] [-g groups]
624 *
625 * This command generates a list of user login-IDs. Specific login-IDs
626 * can be listed, as can logins belonging in specific groups.
627 *
628 * -l logins specifies the login-IDs to display. "logins" is a
629 * comma-list of login-IDs.
630 * -g groups specifies the names of the groups to which a login-ID
631 * must belong before it is included in the generated list.
632 * "groups" is a comma-list of group names.
633 * Exit Codes:
634 * 0 All's well that ends well
635 * 1 Usage error
636 */
637
638 int
main(int argc,char ** argv)639 main(int argc, char **argv)
640 {
641
642 /* Automatic data */
643
644 struct reqgrp *reqgrphead; /* Head of the req'd group list */
645 struct reqgrp *pgrp; /* Current item in the req'd group list */
646 struct reqgrp *qgrp; /* Prev item in the req'd group list */
647 struct reqgrp *rgrp; /* Running ptr for scanning group list */
648 struct reqlogin *reqloginhead; /* Head of req'd login list */
649 struct reqlogin *plogin; /* Current item in the req'd login list */
650 struct reqlogin *qlogin; /* Previous item in the req'd login list */
651 struct reqlogin *rlogin; /* Running ptr for scanning login list */
652 struct passwd *pwent; /* Ptr to an /etc/passwd entry */
653 struct group *grent; /* Ptr to an /etc/group entry */
654 char *token; /* Ptr to a token extracted by strtok() */
655 char **pp; /* Ptr to a member of a group */
656 char *g_arg; /* Ptr to the -g option's argument */
657 char *l_arg; /* Ptr to the -l option's argument */
658 int g_seen; /* FLAG, true if -g on cmd */
659 int l_seen; /* FLAG, TRUE if -l is on the command line */
660 int errflg; /* FLAG, TRUE if there is a command-line problem */
661 int done; /* FLAG, TRUE if the process (?) is complete */
662 int groupcount; /* Number of groups specified by the user */
663 int rc; /* Return code from strcmp() */
664 int c; /* Character returned from getopt() */
665
666
667 /* Initializations */
668 initmsg(argv[0]);
669
670 /* Command-line processing */
671 g_seen = FALSE;
672 l_seen = FALSE;
673 errflg = FALSE;
674 opterr = 0;
675 while (!errflg && ((c = getopt(argc, argv, "g:l:")) != EOF)) {
676
677 /* Case on the option character */
678 switch (c) {
679
680 case 'g':
681 if (g_seen)
682 errflg = TRUE;
683 else {
684 g_seen = TRUE;
685 g_arg = optarg;
686 }
687 break;
688
689 case 'l':
690 if (l_seen)
691 errflg = TRUE;
692 else {
693 l_seen = TRUE;
694 l_arg = optarg;
695 }
696 break;
697
698 default:
699 errflg = TRUE;
700 }
701 }
702
703 /* Write out a usage message if necessary and quit */
704 if (errflg || (optind != argc)) {
705 wrtmsg(MM_ERROR, MM_NULLACT, MM_NULLTAG, USAGE_MSG);
706 exit(1);
707 }
708
709
710 /*
711 * If the -g groups option was on the command line, build a
712 * list containing groups we're to list logins for.
713 */
714 if (g_seen) {
715
716 /* Begin with an empty list */
717 groupcount = 0;
718 reqgrphead = NULL;
719
720 /* Extract the first token putting an element on the list */
721 if ((token = strtok(g_arg, ",")) != NULL) {
722 pgrp = (struct reqgrp *)
723 allocblk(sizeof (struct reqgrp));
724 pgrp->groupname = token;
725 pgrp->found = FALSE;
726 pgrp->next = NULL;
727 groupcount++;
728 reqgrphead = pgrp;
729 qgrp = pgrp;
730
731 /*
732 * Extract subsequent tokens (group names), avoiding
733 * duplicate names (note, list is NOT empty)
734 */
735 while (token = strtok(NULL, ",")) {
736
737 /* Check for duplication */
738 rgrp = reqgrphead;
739 while (rgrp &&
740 (rc = strcmp(token, rgrp->groupname)))
741 rgrp = rgrp->next;
742 if (rc != 0) {
743
744 /* Not a duplicate. Add on the list */
745 pgrp = (struct reqgrp *)
746 allocblk(sizeof (struct reqgrp));
747 pgrp->groupname = token;
748 pgrp->found = FALSE;
749 pgrp->next = NULL;
750 groupcount++;
751 qgrp->next = pgrp;
752 qgrp = pgrp;
753 }
754 }
755 }
756 }
757
758 /*
759 * If -l logins is on the command line, build a list of logins
760 * we're to generate reports for.
761 */
762 if (l_seen) {
763
764 /* Begin with a null list */
765 reqloginhead = NULL;
766
767 /* Extract the first token from the argument to the -l option */
768 if (token = strtok(l_arg, ",")) {
769
770 /* Put the first element in the list */
771 plogin = (struct reqlogin *)
772 allocblk(sizeof (struct reqlogin));
773 plogin->loginname = token;
774 plogin->found = FALSE;
775 plogin->next = NULL;
776 reqloginhead = plogin;
777 qlogin = plogin;
778
779 /*
780 * For each subsequent token in the -l argument's
781 * comma list ...
782 */
783
784 while (token = strtok(NULL, ",")) {
785
786 /* Check for duplication (list is not empty) */
787 rlogin = reqloginhead;
788 while (rlogin &&
789 (rc = strcmp(token, rlogin->loginname)))
790 rlogin = rlogin->next;
791
792 /*
793 * If it's not a duplicate,
794 * add it to the list
795 */
796 if (rc != 0) {
797 plogin = (struct reqlogin *)
798 allocblk(sizeof (struct reqlogin));
799 plogin->loginname = token;
800 plogin->found = FALSE;
801 plogin->next = NULL;
802 qlogin->next = plogin;
803 qlogin = plogin;
804 }
805 }
806 }
807 }
808
809
810 /*
811 * If the user requested that only logins be listed in that belong
812 * to certain groups, compile a list of logins that belong in that
813 * group. If the user also requested specific logins, that list
814 * will be limited to those logins.
815 */
816
817 /* Initialize the login list */
818 initmembers();
819 if (g_seen) {
820
821 /* For each group in the /etc/group file ... */
822 while (grent = getgrent()) {
823
824 /* For each group mentioned with the -g option ... */
825 for (pgrp = reqgrphead; (groupcount > 0) && pgrp;
826 pgrp = pgrp->next) {
827
828 if (pgrp->found == FALSE) {
829
830 /*
831 * If the mentioned group is found in
832 * the /etc/group file ...
833 */
834 if (strcmp(grent->gr_name,
835 pgrp->groupname) == 0) {
836
837 /*
838 * Mark the entry is found,
839 * remembering the group-ID
840 * for later
841 */
842 pgrp->found = TRUE;
843 groupcount--;
844 pgrp->groupID = grent->gr_gid;
845 if (isausergroup(pgrp->groupID))
846 for (pp = grent->gr_mem;
847 *pp; pp++)
848 addmember(*pp);
849 }
850 }
851 }
852 }
853
854 /*
855 * If any groups weren't found, write a message
856 * indicating such, then continue
857 */
858 qgrp = NULL;
859 for (pgrp = reqgrphead; pgrp; pgrp = pgrp->next) {
860 if (!pgrp->found) {
861 wrtmsg(MM_WARNING, MM_NULLACT, MM_NULLTAG,
862 "%s was not found", pgrp->groupname);
863 if (!qgrp)
864 reqgrphead = pgrp->next;
865 else
866 qgrp->next = pgrp->next;
867 } else if (isasystemgroup(pgrp->groupID)) {
868 wrtmsg(MM_WARNING, MM_NULLACT, MM_NULLTAG,
869 "%s is not a user group", pgrp->groupname);
870 if (!qgrp)
871 reqgrphead = pgrp->next;
872 else
873 qgrp->next = pgrp->next;
874 } else
875 qgrp = pgrp;
876 }
877 }
878
879
880 /* Initialize the list of logins to display */
881 initdisp();
882
883
884 /*
885 * Loop through the /etc/passwd file squirelling away the
886 * information we need for the display.
887 */
888 while (pwent = getpwent()) {
889
890 /*
891 * The login from /etc/passwd hasn't been included in
892 * the display yet
893 */
894 done = FALSE;
895
896
897 /*
898 * If the login was explicitly requested, include it in
899 * the display if it is a user login
900 */
901
902 if (l_seen) {
903 for (plogin = reqloginhead; !done && plogin;
904 plogin = plogin->next) {
905 if (strcmp(pwent->pw_name,
906 plogin->loginname) == 0) {
907 plogin->found = TRUE;
908 if (isauserlogin(pwent->pw_uid))
909 adddisp(pwent);
910 else
911 wrtmsg(MM_WARNING, MM_NULLACT,
912 MM_NULLTAG,
913 "%s is not a user login",
914 plogin->loginname);
915 done = TRUE;
916 }
917 }
918 }
919
920
921 /*
922 * If the login-ID isn't already on the list, if its primary
923 * group-ID is one of those groups requested, or it is a member
924 * of the groups requested, include it in the display if it is
925 * a user login (uid >= 100).
926 */
927
928 if (isauserlogin(pwent->pw_uid)) {
929
930 if (!done && g_seen) {
931 for (pgrp = reqgrphead; !done && pgrp;
932 pgrp = pgrp->next)
933 if (pwent->pw_gid == pgrp->groupID) {
934 adddisp(pwent);
935 done = TRUE;
936 }
937 if (!done && isamember(pwent->pw_name)) {
938 adddisp(pwent);
939 done = TRUE;
940 }
941 }
942
943
944 /*
945 * If neither -l nor -g is on the command-line and
946 * the login-ID is a user login, include it in
947 * the display.
948 */
949
950 if (!l_seen && !g_seen)
951 adddisp(pwent);
952 }
953 }
954
955 /* Let the user know about logins they requested that don't exist */
956 if (l_seen)
957 for (plogin = reqloginhead; plogin; plogin = plogin->next)
958 if (!plogin->found)
959 wrtmsg(MM_WARNING, MM_NULLACT, MM_NULLTAG,
960 "%s was not found", plogin->loginname);
961
962
963 /*
964 * Generate a report from this display items we've squirreled away
965 */
966 genreport();
967
968 /*
969 * We're through!
970 */
971 return (0);
972 }
973