nis-util  1.0.D108
lib/space/netgroup/slurp.cc
Go to the documentation of this file.
00001 //
00002 // nis-util - NIS Administration Utilities
00003 // Copyright (C) 2002, 2008, 2009, 2011, 2012 Peter Miller
00004 //
00005 // This program is free software; you can redistribute it and/or modify
00006 // it under the terms of the GNU General Public License as published by
00007 // the Free Software Foundation; either version 2 of the License, or (at
00008 // your option) any later version.
00009 //
00010 // This program is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 // General Public License for more details.
00014 //
00015 // You should have received a copy of the GNU General Public License
00016 // along with this program. If not, see <http://www.gnu.org/licenses/>.
00017 //
00018 
00019 #include <lib/ac/assert.h>
00020 #include <algorithm>
00021 #include <lib/space/netgroup/slurp.h>
00022 #include <lib/colon/passwd/slurp.h>
00023 
00024 
00025 space_netgroup_slurp::~space_netgroup_slurp()
00026 {
00027 }
00028 
00029 
00030 space_netgroup_slurp::space_netgroup_slurp(
00031     const rcstring &a_filename,
00032     const space_hosts_slurp::pointer &a_hosts,
00033     const colon_passwd_slurp::pointer &a_passwd
00034 ) :
00035     space_netgroup(a_filename),
00036     hosts(a_hosts),
00037     passwd(a_passwd)
00038 {
00039 }
00040 
00041 
00042 space_netgroup_slurp::pointer
00043 space_netgroup_slurp::create(const rcstring &a_filename,
00044     const space_hosts_slurp::pointer &a_hosts,
00045     const colon_passwd_slurp::pointer &a_passwd)
00046 {
00047     return pointer(new space_netgroup_slurp(a_filename, a_hosts, a_passwd));
00048 }
00049 
00050 
00051 space_netgroup_slurp::pointer
00052 space_netgroup_slurp::create(const rcstring &a_filename)
00053 {
00054     space_hosts_slurp::pointer a_hosts;
00055     colon_passwd_slurp::pointer a_passwd;
00056     return create(a_filename, a_hosts, a_passwd);
00057 }
00058 
00059 
00060 void
00061 space_netgroup_slurp::process(const record::pointer &rp)
00062 {
00063     //
00064     // Stash the record.
00065     //
00066     by_name_t::iterator it1 = by_name.find(rp->get_name());
00067     if (it1 != by_name.end())
00068     {
00069         error
00070         (
00071             rp->get_source_location(),
00072             "duplicate %s netgroup...",
00073         rp->get_name().quote_c().c_str()
00074         );
00075         error
00076         (
00077             it1->second->get_source_location(),
00078             "...here is the first definition of the %s netgroup",
00079             it1->second->get_name().quote_c().c_str()
00080         );
00081     }
00082     else
00083     {
00084         by_name.insert(by_name_t::value_type(rp->get_name(), rp));
00085     }
00086 
00087     //
00088     // Check the members.
00089     //
00090     const record::members_t &members = rp->get_members();
00091     for
00092     (
00093         record::members_t::const_iterator it = members.begin();
00094         it != members.end();
00095         ++it
00096     )
00097     {
00098         //
00099         // Can't check references until all the groups have been
00100         // read in.
00101         //
00102         if (!it->reference_to_netgroup.empty())
00103             continue;
00104 
00105         //
00106         // Check the user name.
00107         //
00108         if
00109         (
00110             passwd
00111         &&
00112             !it->user_any()
00113         &&
00114             !it->user_ignore()
00115         &&
00116             !passwd->query_by_name(it->user)
00117         )
00118         {
00119             error
00120             (
00121                 rp->get_source_location(),
00122                 "user %s unknown",
00123                 it->user.quote_c().c_str()
00124             );
00125         }
00126 
00127         //
00128         // Check the host name.
00129         //
00130         if
00131         (
00132             hosts
00133         &&
00134             !it->host_any()
00135         &&
00136             !it->host_ignore()
00137         &&
00138             !hosts->query_by_name(it->host)
00139         )
00140         {
00141             error
00142             (
00143                 rp->get_source_location(),
00144                 "host %s unknown",
00145                 it->host.quote_c().c_str()
00146             );
00147         }
00148     }
00149 
00150     //
00151     // Check the line length.
00152     //
00153     if (rp->representation().size() >= 512)
00154     {
00155         error
00156         (
00157             rp->get_source_location(),
00158             "line too long (NIS limits records to 511 bytes, "
00159             "and NIS+ limits record to 1024 bytes)"
00160         );
00161     }
00162 }
00163 
00164 
00165 static bool
00166 append_unique(space_netgroup::record::members_t &elp,
00167     const space_netgroup::entry_t &e)
00168 {
00169     for
00170     (
00171         space_netgroup::record::members_t::const_iterator it = elp.begin();
00172         it != elp.end();
00173         ++it
00174     )
00175     {
00176         if (*it == e)
00177         {
00178             return false;
00179         }
00180     }
00181     elp.push_back(e);
00182     return true;
00183 }
00184 
00185 
00186 static bool
00187 append_unique(space_netgroup::record::members_t &elp,
00188     const space_netgroup::record::members_t &elp2)
00189 {
00190     bool result = false;
00191     for
00192     (
00193         space_netgroup::record::members_t::const_iterator it2 = elp2.begin();
00194         it2 != elp2.end();
00195         ++it2
00196     )
00197     {
00198         if (append_unique(elp, *it2))
00199             result = true;
00200     }
00201     return result;
00202 }
00203 
00204 
00205 void
00206 space_netgroup_slurp::read_and_process(void)
00207 {
00208     //
00209     // Read the file contents.
00210     //
00211     for (;;)
00212     {
00213         record::pointer rp = get();
00214         if (!rp)
00215         {
00216             break;
00217         }
00218         process(rp);
00219     }
00220 
00221     //
00222     // Check that cross references are OK.
00223     // (Sorting the keys gives more consistent output, which reduces
00224     // the false negatives when testing.  Also, std::map does it for free.)
00225     //
00226     name_list_t keys = keys_by_name();
00227     // std::sort(keys.begin(), keys.end());
00228     for (name_list_t::iterator it = keys.begin(); it != keys.end(); ++it)
00229     {
00230         rcstring key = *it;
00231         record::pointer rp = query_by_name(key);
00232         assert(rp);
00233         if (!rp)
00234             continue;
00235         const record::members_t &members = rp->get_members();
00236         for
00237         (
00238             record::members_t::const_iterator mit = members.begin();
00239             mit != members.end();
00240             ++mit
00241         )
00242         {
00243             //
00244             // Ignore group members which are not references to other groups.
00245             //
00246             rcstring name2 = mit->reference_to_netgroup;
00247             if (name2.empty())
00248                 continue;
00249 
00250             //
00251             // Make sure referenced groups exist.
00252             //
00253             record::pointer rp2 = query_by_name(name2);
00254             if (!rp2)
00255             {
00256                 error
00257                 (
00258                     rp->get_source_location(),
00259                     "netgroup %s references netgroup %s that does not exist",
00260                     rp->get_name().quote_c().c_str(),
00261                     name2.quote_c().c_str()
00262                 );
00263             }
00264         }
00265     }
00266 
00267     //
00268     // Compute the closure of by_name.
00269     // (Sorting the keys gives more consistent output, which reduces the false
00270     // negatives when testing.  By using std::map, we get that sort for free.)
00271     //
00272     // FIXME: Unfortunately, this has O(n**3) behaviour.  Sites with
00273     // many large netgroups may need for this to use the algorithm in
00274     // Knuth to speed things up.
00275     //
00276     typedef std::map<rcstring, record::members_t> closure_t;
00277     closure_t closure;
00278     for
00279     (
00280         by_name_t::const_iterator bnit = by_name.begin();
00281         bnit != by_name.end();
00282         ++bnit
00283     )
00284     {
00285         closure.insert
00286         (
00287             closure_t::value_type(bnit->first, bnit->second->get_members())
00288         );
00289     }
00290     for (;;)
00291     {
00292         //
00293         // We keep iterating until there are no more changes.
00294         //
00295         bool changed = false;
00296 
00297         //
00298         // For each netgroup, make sure we have everything covered.
00299         //
00300         for
00301         (
00302             name_list_t::const_iterator kit1 = keys.begin();
00303             kit1 != keys.end();
00304             ++kit1
00305         )
00306         {
00307             //
00308             // Look at each netgroup entry, and if it references another
00309             // netgroup, add all its entries to this one.
00310             //
00311             rcstring key1 = *kit1;
00312             closure_t::iterator elp1 = closure.find(key1);
00313             if (elp1 == closure.end())
00314                 continue;
00315             const record::members_t &members = elp1->second;
00316             record::members_t new_members = members;
00317             for
00318             (
00319                 record::members_t::const_iterator eit = members.begin();
00320                 eit != members.end();
00321                 ++eit
00322             )
00323             {
00324                 rcstring key2 = eit->reference_to_netgroup;
00325                 if (key2.empty())
00326                     continue;
00327 
00328                 // short circuit reference to self
00329                 if (key2 == key1)
00330                     continue;
00331                 closure_t::const_iterator elp2 = closure.find(key2);
00332 
00333                 //
00334                 // Ignore netgroups that don't exist.
00335                 //
00336                 if (elp2 == closure.end())
00337                     continue;
00338 
00339                 //
00340                 // If there is stuff in *elp2 that isn't in *elp1,
00341                 // then something changed, and we need to iterate.
00342                 //
00343                 if (append_unique(new_members, elp2->second))
00344                 {
00345                     changed = true;
00346                 }
00347             }
00348             elp1->second = new_members;
00349         }
00350 
00351         //
00352         // If this iteration found nothing different, we are done.
00353         //
00354         if (!changed)
00355             break;
00356     }
00357 
00358     //
00359     // Iterate over the closure, checking for self references, and also
00360     // filling in the by_user and by_host instance variables.
00361     //
00362     for
00363     (
00364         closure_t::const_iterator cit = closure.begin();
00365         cit != closure.end();
00366         ++cit
00367     )
00368     {
00369         for
00370         (
00371             record::members_t::const_iterator mit = cit->second.begin();
00372             mit != cit->second.end();
00373             ++mit
00374         )
00375         {
00376             //
00377             // Check for self recursion.  We do this once, rather than
00378             // risk the same error being issued multiple times by the
00379             // loop that computes the closure of by_name.
00380             //
00381             if (cit->first == mit->reference_to_netgroup)
00382             {
00383                 record::pointer rp = query_by_name(cit->first);
00384                 assert(rp);
00385                 if (rp)
00386                 {
00387                     error
00388                     (
00389                         rp->get_source_location(),
00390                         "netgroup %s contains a recursive reference to itself",
00391                         rp->get_name().quote_c().c_str()
00392                     );
00393                     break;
00394                 }
00395             }
00396 
00397             //
00398             // Ignore cross references, we have resolved them all, now.
00399             //
00400             if (!mit->reference_to_netgroup.empty())
00401                 continue;
00402 
00403             //
00404             // Stash the by_host information.
00405             //
00406             if (mit->host_indexing())
00407             {
00408                 rcstring hostname(mit->host_any() ? "*" : mit->host);
00409                 by_host_t::iterator nlp = by_host.find(hostname);
00410                 if (nlp == by_host.end())
00411                 {
00412                     name_list_t names;
00413                     names.push_back(cit->first);
00414                     by_host.insert(by_host_t::value_type(hostname, names));
00415                 }
00416                 else
00417                     nlp->second.push_back(cit->first);
00418             }
00419 
00420             //
00421             // Stash the by_user information.
00422             //
00423             if (mit->user_indexing())
00424             {
00425                 rcstring username(mit->user_any() ? "*" : mit->user);
00426                 by_user_t::iterator nlp = by_user.find(username);
00427                 if (nlp == by_user.end())
00428                 {
00429                     name_list_t names;
00430                     names.push_back(cit->first);
00431                     by_user.insert(by_user_t::value_type(username, names));
00432                 }
00433                 else
00434                     nlp->second.push_back(cit->first);
00435             }
00436         }
00437     }
00438 
00439     //
00440     // Close the file.
00441     //
00442     close();
00443 }
00444 
00445 
00446 space_netgroup_slurp::name_list_t
00447 space_netgroup_slurp::keys_by_name(void)
00448     const
00449 {
00450     name_list_t result;
00451     for
00452     (
00453         by_name_t::const_iterator it = by_name.begin();
00454         it != by_name.end();
00455         ++it
00456     )
00457         result.push_back(it->first);
00458     return result;
00459 }
00460 
00461 
00462 space_netgroup::record::pointer
00463 space_netgroup_slurp::query_by_name(const rcstring &key)
00464     const
00465 {
00466     by_name_t::const_iterator it = by_name.find(key);
00467     if (it == by_name.end())
00468         return record::pointer();
00469     return it->second;
00470 }
00471 
00472 
00473 space_netgroup_slurp::name_list_t
00474 space_netgroup_slurp::keys_by_host(void)
00475     const
00476 {
00477     name_list_t result;
00478     for
00479     (
00480         by_host_t::const_iterator it = by_host.begin();
00481         it != by_host.end();
00482         ++it
00483     )
00484         result.push_back(it->first);
00485     return result;
00486 }
00487 
00488 
00489 space_netgroup_slurp::name_list_t
00490 space_netgroup_slurp::query_by_host(const rcstring &key)
00491     const
00492 {
00493     by_host_t::const_iterator it = by_host.find(key);
00494     if (it == by_host.end())
00495         return name_list_t();
00496     return it->second;
00497 }
00498 
00499 
00500 space_netgroup_slurp::name_list_t
00501 space_netgroup_slurp::keys_by_user(void)
00502     const
00503 {
00504     name_list_t result;
00505     for
00506     (
00507         by_user_t::const_iterator it = by_user.begin();
00508         it != by_user.end();
00509         ++it
00510     )
00511         result.push_back(it->first);
00512     return result;
00513 }
00514 
00515 
00516 space_netgroup_slurp::name_list_t
00517 space_netgroup_slurp::query_by_user(const rcstring &key)
00518     const
00519 {
00520     by_user_t::const_iterator it = by_user.find(key);
00521     if (it == by_user.end())
00522         return name_list_t();
00523     return it->second;
00524 }
00525 
00526 
00527 // vim: set ts=8 sw=4 et :