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