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