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