home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / c / cops_104.zip / cops_104 / perl / pass.cache.pl < prev    next >
Perl Script  |  1992-03-10  |  11KB  |  445 lines

  1. #
  2. #   Routines for reading and caching user and group information.  These
  3. # are used in multiple programs... it caches the info once, then hopefully
  4. # won't be used again.
  5. #
  6. #  Steve Romig, May 1991.
  7. #
  8. # Provides a bunch of routines and a bunch of arrays.  Routines 
  9. # (and their usage):
  10. #
  11. #    load_passwd_info($use_getent, $file_name)
  12. #
  13. #    loads user information into the %uname* and %uid* arrays 
  14. #    (see below).  
  15. #
  16. #    If $use_getent is non-zero:
  17. #        get the info via repeated 'getpwent' calls.  This can be
  18. #        *slow* on some hosts, especially if they are running as a
  19. #        YP (NIS) client.
  20. #    If $use_getent is 0:
  21. #        if $file_name is "", then get the info from reading the 
  22. #        results of "ypcat passwd" and from /etc/passwd.  Otherwise, 
  23. #        read the named file.  The file should be in passwd(5) 
  24. #        format.
  25. #
  26. #    load_group_info($use_gentent, $file_name)
  27. #
  28. #    is similar to load_passwd_info.
  29. #
  30. # Information is stored in several convenient associative arrays:
  31. #
  32. #   %uname2shell    Assoc array, indexed by user name, value is 
  33. #            shell for that user name.
  34. #
  35. #   %uname2dir        Assoc array, indexed by user name, value is
  36. #            home directory for that user name.
  37. #
  38. #   %uname2uid        Assoc array, indexed by name, value is uid for 
  39. #            that uid.
  40. #            
  41. #   %uname2passwd    Assoc array, indexed by name, value is password
  42. #            for that user name.
  43. #
  44. #   %uid2names        Assoc array, indexed by uid, value is list of
  45. #            user names with that uid, in form "name name
  46. #            name...". 
  47. #
  48. #   %gid2members    Assoc array, indexed by gid, value is list of
  49. #            group members in form "name name name..."
  50. #
  51. #   %gname2gid        Assoc array, indexed by group name, value is
  52. #            matching gid.
  53. #
  54. #   %gid2names        Assoc array, indexed by gid, value is the
  55. #            list of group names with that gid in form 
  56. #            "name name name...".
  57. #
  58. # You can also use routines named the same as the arrays - pass the index 
  59. # as the arg, get back the value.  If you use this, get{gr|pw}{uid|gid|nam} 
  60. # will be used to lookup entries that aren't found in the cache.
  61. #
  62. # To be done:
  63. #    probably ought to add routines to deal with full names.
  64. #    maybe there ought to be some anal-retentive checking of password 
  65. #    and group entries.
  66. #    probably ought to cache get{pw|gr}{nam|uid|gid} lookups also.
  67. #    probably ought to avoid overwriting existing entries (eg, duplicate 
  68. #       names in password file would collide in the tables that are 
  69. #    indexed by name).
  70. #
  71. # Disclaimer:
  72. #    If you use YP and you use netgroup entries such as 
  73. #    +@servers::::::
  74. #    +:*:::::/usr/local/utils/messages
  75. #    then loading the password file in with &load_passwd_info(0) will get 
  76. #    you mostly correct YP stuff *except* that it won't do the password and 
  77. #    shell substitutions as you'd expect.  You might want to use 
  78. #    &load_passwd_info(1) instead to use getpwent calls to do the lookups, 
  79. #    which would be more correct.
  80. #
  81.  
  82. package main;
  83.  
  84. $PASSWD = '/etc/passwd' unless defined $PASSWD;
  85.  
  86. require 'pathconf.pl';
  87.  
  88. %uname2shell = ();
  89. %uname2dir = ();
  90. %uname2uid = ();
  91. %uname2passwd = ();
  92. %uid2names = ();
  93. %gid2members = ();
  94. %gname2gid = ();
  95. %gid2names = ();
  96.  
  97. $DOMAINNAME = "/bin/domainname" unless defined $DOMAINNAME;
  98. $YPCAT = "/bin/ypcat" unless defined $YPCAT;
  99.  
  100. $yptmp = "./yptmp.$$";
  101.  
  102. $passwd_loaded = 0;        # flags to use to avoid reloading everything
  103. $group_loaded = 0;        # unnecessarily...
  104.  
  105. #
  106. # We provide routines for getting values from the data structures as well.
  107. # These are named after the data structures they cache their data in.  Note 
  108. # that they will all generate password and group file lookups via getpw* 
  109. # and getgr* if they can't find info in the cache, so they will work
  110. # "right" even if load_passwd_info and load_group_info aren't called to 
  111. # preload the caches.
  112. #
  113. # I should point out, however, that if you don't call load_*_info to preload
  114. # the cache, uid2names, gid2names and gid2members *will not* be complete, since 
  115. # you must read the entire password and group files to get a complete picture.
  116. # This might be acceptable in some cases, so you can skip the load_*_info
  117. # calls if you know what you are doing...
  118. #
  119. sub uname2shell {
  120.     local($key) = @_;
  121.  
  122.     if (! defined($uname2shell{$key})) {
  123.     &add_pw_info(getpwnam($key));
  124.     }
  125.  
  126.     return($uname2shell{$key});
  127. }
  128.  
  129. sub uname2dir {
  130.     local($key) = @_;
  131.     local(@pw_info);
  132.  
  133.     if (! defined($uname2dir{$key})) {
  134.     &add_pw_info(getpwnam($key));
  135.     }
  136.  
  137.     return($uname2dir{$key});
  138. }
  139.  
  140. sub uname2uid {
  141.     local($key) = @_;
  142.     local(@pw_info);
  143.  
  144.     if (! defined($uname2uid{$key})) {
  145.     &add_pw_info(getpwnam($key));
  146.     }
  147.  
  148.     return($uname2uid{$key});
  149. }
  150.  
  151. sub uname2passwd {
  152.     local($key) = @_;
  153.     local(@pw_info);
  154.  
  155.     if (! defined($uname2passwd{$key})) {
  156.     &add_pw_info(getpwnam($key));
  157.     }
  158.  
  159.     return($uname2passwd{$key});
  160. }
  161.  
  162. sub uid2names {
  163.     local($key) = @_;
  164.     local(@pw_info);
  165.  
  166.     if (! defined($uid2names{$key})) {
  167.     &add_pw_info(getpwuid($key));
  168.     }
  169.  
  170.     return($uid2names{$key});
  171. }
  172.  
  173. sub gid2members {
  174.     local($key) = @_;
  175.     local(@gr_info);
  176.  
  177.     if (! defined($gid2members{$key})) {
  178.     &add_gr_info(getgrgid($key));
  179.     }
  180.  
  181.     return($gid2members{$key});
  182. }
  183.  
  184. sub gname2gid {
  185.     local($key) = @_;
  186.     local(@gr_info);
  187.  
  188.     if (! defined($gname2gid{$key})) {
  189.     &add_gr_info(getgrnam($key));
  190.     }
  191.  
  192.     return($gname2gid{$key});
  193. }
  194.  
  195. sub gid2names {
  196.     local($key) = @_;
  197.     local(@gr_info);
  198.  
  199.     if (! defined($gid2names{$key})) {
  200.     &add_gr_info(getgrgid($key));
  201.     }
  202.  
  203.     return($gid2names{$key});
  204. }
  205.  
  206. #
  207. # Update user information for the user named $name.  We cache the password, 
  208. # uid, login group, home directory and shell.
  209. #
  210.  
  211. sub add_pw_info {
  212.     local($name, $passwd, $uid, $gid) = @_;
  213.     local($dir, $shell);
  214.  
  215. #
  216. # Ugh!  argh...yech...sigh.  If we use getpwent, we get back 9 elts, 
  217. # if we parse /etc/passwd directly we get 7.  Pick off the last 2 and 
  218. # assume that they are the $directory and $shell.  
  219. #
  220.     $num = ( $#_ >= 7 ? 8 : 6 );
  221.     $dir = $_[$num - 1];
  222.     $shell = $_[$num] || '/bin/sh';
  223.  
  224.  
  225.     if ($name ne "") {
  226.     $uname2shell{$name} = $shell;
  227.     $uname2dir{$name} = $dir;
  228.     $uname2uid{$name} = $uid;
  229.     $uname2passwd{$name} = $passwd;
  230.  
  231.     if ($gid ne "") {
  232.         # fixme: should probably check for duplicates...sigh
  233.  
  234.         if (defined($gid2members{$gid})) {
  235.         $gid2members{$gid} .= " $name";
  236.         } else {
  237.         $gid2members{$gid} = $name;
  238.         }
  239.     }
  240.  
  241.     if ($uid ne "") {
  242.         if (defined($uid2names{$uid})) {
  243.         $uid2names{$uid} .= " $name";
  244.         } else {
  245.         $uid2names{$uid} = $name;
  246.         }
  247.     }
  248.     }
  249. }
  250.  
  251. #
  252. # Update group information for the group named $name.  We cache the gid 
  253. # and the list of group members.
  254. #
  255.  
  256. sub add_gr_info {
  257.     local($name, $passwd, $gid, $members) = @_;
  258.  
  259.     if ($name ne "") {
  260.     $gname2gid{$name} = $gid;
  261.  
  262.     if ($gid ne "") {
  263.         if (defined($gid2names{$gid})) {
  264.         $gid2names{$gid} .= " $name";
  265.         } else {
  266.         $gid2names{$gid} = $name;
  267.         }
  268.  
  269.         # fixme: should probably check for duplicates
  270.  
  271.         $members = join(' ', split(/[, \t]+/, $members));
  272.  
  273.         if (defined($gid2members{$gid})) {
  274.         $gid2members{$gid} .= " " . $members;
  275.         } else {
  276.         $gid2members{$gid} = $members;
  277.         }
  278.     }
  279.     }
  280. }
  281.  
  282. #
  283. # We need to suck in the entire group and password files so that we can 
  284. # make the %uid2names, %gid2members and %gid2names lists complete.  Otherwise,
  285. # we would just read the entries as needed with getpw* and cache the results.
  286. # Sigh.
  287. #
  288. # There are several ways that we might find the info.  If $use_getent is 1, 
  289. # then we just use getpwent and getgrent calls to read the info in.
  290. #
  291. # That isn't real efficient if you are using YP (especially on a YP client), so
  292. # if $use_getent is 0, we can use ypcat to get a copy of the passwd and
  293. # group maps in a fairly efficient manner.  If we do this we have to also read
  294. # the local /etc/{passwd,group} files to complete our information.  If we aren't 
  295. # using YP, we just read the local pasword and group files.
  296. #
  297. sub load_passwd_info {
  298.     local($use_getent, $file_name) = @_;
  299.     local(@pw_info);
  300.  
  301.     if ($passwd_loaded) {
  302.     return;
  303.     }
  304.  
  305.     $passwd_loaded = 1;
  306.  
  307.     if ($'GET_PASSWD) {
  308.     open(GFILE, "$'GET_PASSWD|") || die "can't $'GET_PASSWD";
  309.     while (<GFILE>) {
  310.         chop;
  311.         &add_pw_info(split(/:/));
  312.         }
  313.     close(GFILE);
  314.     }
  315.     else {
  316.  
  317.     if ($use_getent) {
  318.     #
  319.     # Use getpwent to get the info from the system, and add_pw_info to 
  320.     # cache it.
  321.     #
  322.     while (@pw_info = getpwent) {
  323.         &add_pw_info(@pw_info);
  324.     }
  325.  
  326.     endpwent;
  327.  
  328.     return;
  329.     } elsif ($file_name eq "") {
  330.     chop($has_yp = `$DOMAINNAME`);
  331.     if ($has_yp) {
  332.         #
  333.         # If we have YP (NIS), then use ypcat to get the stuff from the 
  334.         # map.@
  335.         #
  336.         system("$YPCAT passwd > $yptmp 2> /dev/null");
  337.         if (-s $yptmp) {
  338.             open(FILE, "$YPCAT passwd|") ||
  339.               die "can't 'ypcat passwd'";
  340.             while (<FILE>) {
  341.             chop;
  342.             &add_pw_info(split(/:/));
  343.                 }
  344.             }
  345.         close(FILE);
  346.     }
  347.  
  348.     #
  349.     # We have to read /etc/passwd no matter what...
  350.     #
  351.     $file_name = "/etc/passwd";
  352.     }
  353.  
  354.     open(FILE, $file_name) ||
  355.       die "can't open $file_name";
  356.  
  357.     while (<FILE>) {
  358.     chop;
  359.         
  360.     if ($_ !~ /^\+/) {
  361.         &add_pw_info(split(/:/));
  362.     }
  363.  
  364.     # fixme: if the name matches +@name, then this is a wierd 
  365.     # netgroup thing, and we aren't dealing with it right.  might want
  366.     # to warn the poor user...suggest that he use the use_getent 
  367.     # method instead.
  368.     }
  369.     }
  370.  
  371.     close(FILE);
  372. }
  373.  
  374. sub load_group_info {
  375.     local($use_getent, $file_name) = @_;
  376.     local(@gr_info);
  377.  
  378.     if ($group_loaded) {
  379.     return;
  380.     }
  381.  
  382.     $group_loaded = 1;
  383.  
  384.     if ($use_getent) {
  385.     #
  386.     # Use getgrent to get the info from the system, and add_gr_info to 
  387.     # cache it.
  388.     #
  389.     while ((@gr_info = getgrent()) != 0) {
  390.         &add_gr_info(@gr_info);
  391.     }
  392.  
  393.     endgrent();
  394.  
  395.     return();
  396.     } elsif ($file_name eq "") {
  397.     chop($has_yp = `$DOMAINNAME`);
  398.     if ($has_yp) {
  399.         #
  400.         # If we have YP (NIS), then use ypcat to get the stuff from the 
  401.         # map.
  402.         #
  403.         system("$YPCAT passwd > $yptmp 2> /dev/null");
  404.         if (-s $yptmp) {
  405.             open(FILE, "$YPCAT group|") ||
  406.               die "can't 'ypcat group'";
  407.             while (<FILE>) {
  408.             chop;
  409.             &add_gr_info(split(/:/));
  410.                 }
  411.             close(FILE);
  412.         }
  413.     }
  414.  
  415.     #
  416.     # We have to read /etc/group no matter what...
  417.     #
  418.     $file_name = "/etc/group";
  419.     }
  420.  
  421.     open(FILE, $file_name) ||
  422.       die "can't open $file_name";
  423.  
  424.     while (<FILE>) {
  425.     chop;
  426.     if ($_ !~ /^\+/) {
  427.         &add_gr_info(split(/:/));
  428.     }
  429.  
  430.     # fixme: if the name matches +@name, then this is a wierd 
  431.     # netgroup thing, and we aren't dealing with it right.  might want
  432.     # to warn the poor user...suggest that he use the use_getent 
  433.     # method instead.
  434.     }
  435.  
  436.     close(FILE);
  437. }
  438.  
  439. # Load the password stuff -- Do NOT take this out!
  440. &'load_passwd_info(0,$PASSWD);
  441.  
  442. unlink $yptmp;
  443.  
  444. 1;
  445.