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 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 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 * 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 * 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 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 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 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 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 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 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 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