sbuild-chroot-config.cc

Go to the documentation of this file.
00001 /* Copyright © 2005-2006  Roger Leigh <rleigh@debian.org>
00002  *
00003  * schroot is free software; you can redistribute it and/or modify it
00004  * under the terms of the GNU General Public License as published by
00005  * the Free Software Foundation; either version 2 of the License, or
00006  * (at your option) any later version.
00007  *
00008  * schroot is distributed in the hope that it will be useful, but
00009  * WITHOUT ANY WARRANTY; without even the implied warranty of
00010  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00011  * General Public License for more details.
00012  *
00013  * You should have received a copy of the GNU General Public License
00014  * along with this program; if not, write to the Free Software
00015  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
00016  * MA  02111-1307  USA
00017  *
00018  *********************************************************************/
00019 
00020 #include <config.h>
00021 
00022 #include "sbuild-chroot.h"
00023 #include "sbuild-chroot-source.h"
00024 #include "sbuild-chroot-config.h"
00025 #include "sbuild-lock.h"
00026 
00027 #include <cerrno>
00028 #include <cstdlib>
00029 #include <cstring>
00030 #include <iostream>
00031 
00032 #include <ext/stdio_filebuf.h>
00033 
00034 #include <sys/types.h>
00035 #include <sys/stat.h>
00036 #include <dirent.h>
00037 #include <fcntl.h>
00038 #include <unistd.h>
00039 
00040 #include <boost/format.hpp>
00041 
00042 using std::endl;
00043 using boost::format;
00044 using namespace sbuild;
00045 
00046 namespace
00047 {
00048 
00049   typedef std::pair<chroot_config::error_code,const char *> emap;
00050 
00055   emap init_errors[] =
00056     {
00057       // TRANSLATORS: %1% = chroot alias name
00058       // TRANSLATORS: %4% = chroot name
00059       emap(chroot_config::ALIAS_EXIST,     N_("Alias '%1%' already associated with '%4%' chroot")),
00060       emap(chroot_config::CHROOT_NOTFOUND, N_("No such chroot")),
00061       // TRANSLATORS: %1% = chroot name
00062       emap(chroot_config::CHROOT_EXIST,    N_("A chroot or alias '%1%' already exists by this name")),
00063       emap(chroot_config::DIR_OPEN,        N_("Failed to open directory")),
00064       emap(chroot_config::FILE_NOTREG,     N_("File is not a regular file")),
00065       emap(chroot_config::FILE_OPEN,       N_("Failed to open file")),
00066       emap(chroot_config::FILE_OWNER,      N_("File is not owned by user root")),
00067       emap(chroot_config::FILE_PERMS,      N_("File has write permissions for others")),
00068       emap(chroot_config::FILE_STAT,       N_("Failed to stat file"))
00069     };
00070 
00071   bool chroot_alphasort (sbuild::chroot::ptr const& c1,
00072                          sbuild::chroot::ptr const& c2)
00073   {
00074     return c1->get_name() < c2->get_name();
00075   }
00076 
00077 }
00078 
00079 template<>
00080 error<chroot_config::error_code>::map_type
00081 error<chroot_config::error_code>::error_strings
00082 (init_errors,
00083  init_errors + (sizeof(init_errors) / sizeof(init_errors[0])));
00084 
00085 chroot_config::chroot_config ():
00086   chroots()
00087 {
00088 }
00089 
00090 chroot_config::chroot_config (std::string const& file,
00091                               bool               active):
00092   chroots()
00093 {
00094   add(file, active);
00095 }
00096 
00097 chroot_config::~chroot_config ()
00098 {
00099 }
00100 
00101 void
00102 chroot_config::add (std::string const& location,
00103                     bool               active)
00104 {
00105   struct stat statbuf;
00106   if (stat(location.c_str(), &statbuf) == 0 && S_ISDIR(statbuf.st_mode))
00107     add_config_directory(location, active);
00108   else
00109     add_config_file(location, active);
00110 }
00111 
00112 void
00113 chroot_config::add_config_file (std::string const& file,
00114                                 bool               active)
00115 {
00116   log_debug(DEBUG_NOTICE) << "Loading config file: " << file << endl;
00117 
00118   load_data(file, active);
00119 }
00120 
00121 void
00122 chroot_config::add_config_directory (std::string const& dir,
00123                                      bool               active)
00124 {
00125   log_debug(DEBUG_NOTICE) << "Loading config directory: " << dir << endl;
00126 
00127   if (dir.empty())
00128     return;
00129 
00130   DIR *d = opendir(dir.c_str());
00131   if (d == 0)
00132     {
00133       throw error(dir, DIR_OPEN, strerror(errno));
00134     }
00135 
00136   struct dirent *de = 0;
00137   while ((de = readdir(d)) != 0)
00138     {
00139       std::string filename = dir + "/" + de->d_name;
00140 
00141       struct stat statbuf;
00142       if (stat(filename.c_str(), &statbuf) < 0)
00143         {
00144           error e(filename, FILE_STAT, strerror(errno));
00145           log_exception_warning(e);
00146           continue;
00147         }
00148 
00149       if (!S_ISREG(statbuf.st_mode))
00150         {
00151           if (!(strcmp(de->d_name, ".") == 0 ||
00152                 strcmp(de->d_name, "..") == 0))
00153             {
00154               error e (filename, FILE_NOTREG);
00155               log_exception_warning(e);
00156             }
00157           continue;
00158         }
00159 
00160       load_data(filename, active);
00161     }
00162 }
00163 
00164 void
00165 chroot_config::add (chroot::ptr&   chroot,
00166                     keyfile const& kconfig)
00167 {
00168   std::string const& name = chroot->get_name();
00169 
00170   // Make sure insertion will succeed.
00171   if (this->chroots.find(name) == this->chroots.end() &&
00172       this->aliases.find(name) == this->aliases.end())
00173     {
00174       // Set up chroot.
00175       this->chroots.insert(std::make_pair(name, chroot));
00176       this->aliases.insert(std::make_pair(name, name));
00177 
00178       // Set up aliases.
00179       string_list const& aliases = chroot->get_aliases();
00180       for (string_list::const_iterator pos = aliases.begin();
00181            pos != aliases.end();
00182            ++pos)
00183         {
00184           try
00185             {
00186               if (this->aliases.insert(std::make_pair(*pos, name))
00187                   .second == false)
00188                 {
00189                   string_map::const_iterator dup = this->aliases.find(*pos);
00190                   // Don't warn if alias is for chroot of same name.
00191                   if (dup == this->aliases.end() ||
00192                       name != dup->first)
00193                     {
00194                       const char *const key("aliases");
00195                       unsigned int line = kconfig.get_line(name, key);
00196 
00197                       if (dup == this->aliases.end())
00198                         {
00199                           error e(*pos, ALIAS_EXIST);
00200                           if (line)
00201                             throw keyfile::error(line, name, key,
00202                                                  keyfile::PASSTHROUGH_LGK, e);
00203                           else
00204                             throw keyfile::error(name, key,
00205                                                  keyfile::PASSTHROUGH_GK, e);
00206                         }
00207                       else
00208                         {
00209                           error e(dup->first, ALIAS_EXIST, dup->second);
00210                           if (line)
00211                             throw keyfile::error(line, name, key,
00212                                                  keyfile::PASSTHROUGH_LGK, e);
00213                           else
00214                             throw keyfile::error(name, key,
00215                                                  keyfile::PASSTHROUGH_GK, e);
00216                         }
00217                     }
00218                 }
00219             }
00220           catch (std::runtime_error const& e)
00221             {
00222               log_exception_warning(e);
00223             }
00224         }
00225     }
00226   else
00227     {
00228       unsigned int line = kconfig.get_line(name);
00229 
00230       error e(name, CHROOT_EXIST);
00231 
00232       if (line)
00233         {
00234           keyfile::error ke(line, name, keyfile::PASSTHROUGH_LG, e);
00235           ke.set_reason(_("Duplicate names are not allowed"));
00236           throw ke;
00237         }
00238       else
00239         {
00240           keyfile::error ke(name, keyfile::PASSTHROUGH_G, e);
00241           ke.set_reason(_("Duplicate names are not allowed"));
00242           throw ke;
00243         }
00244     }
00245 }
00246 
00247 chroot_config::chroot_list
00248 chroot_config::get_chroots () const
00249 {
00250   chroot_list ret;
00251 
00252   for (chroot_map::const_iterator pos = this->chroots.begin();
00253        pos != this->chroots.end();
00254        ++pos)
00255     ret.push_back(pos->second);
00256 
00257   std::sort(ret.begin(), ret.end(), chroot_alphasort);
00258 
00259   return ret;
00260 }
00261 
00262 const sbuild::chroot::ptr
00263 chroot_config::find_chroot (std::string const& name) const
00264 {
00265   chroot_map::const_iterator pos = this->chroots.find(name);
00266 
00267   if (pos != this->chroots.end())
00268     return pos->second;
00269   else
00270     {
00271       chroot *null_chroot = 0;
00272       return chroot::ptr(null_chroot);
00273     }
00274 }
00275 
00276 const sbuild::chroot::ptr
00277 chroot_config::find_alias (std::string const& name) const
00278 {
00279   string_map::const_iterator pos = this->aliases.find(name);
00280 
00281   if (pos != this->aliases.end())
00282     return find_chroot(pos->second);
00283   else
00284     {
00285       chroot *null_chroot = 0;
00286       return chroot::ptr(null_chroot);
00287     }
00288 }
00289 
00290 string_list
00291 chroot_config::get_chroot_list () const
00292 {
00293   string_list ret;
00294 
00295   for (string_map::const_iterator pos = this->aliases.begin();
00296        pos != this->aliases.end();
00297        ++pos)
00298     ret.push_back(pos->first);
00299 
00300   std::sort(ret.begin(), ret.end());
00301 
00302   return ret;
00303 }
00304 
00305 void
00306 chroot_config::print_chroot_list (std::ostream& stream) const
00307 {
00308   string_list chroots = get_chroot_list();
00309 
00310   for (string_list::const_iterator pos = chroots.begin();
00311        pos != chroots.end();
00312        ++pos)
00313     stream << *pos << "\n";
00314   stream << std::flush;
00315 }
00316 
00317 void
00318 chroot_config::print_chroot_list_simple (std::ostream& stream) const
00319 {
00320   stream << _("Available chroots: ");
00321 
00322   for (chroot_map::const_iterator pos = this->chroots.begin();
00323        pos != this->chroots.end();
00324        ++pos)
00325     {
00326       stream << pos->second->get_name();
00327       string_list const& aliases = pos->second->get_aliases();
00328       if (!aliases.empty())
00329         {
00330           stream << " [";
00331           for (string_list::const_iterator alias = aliases.begin();
00332                alias != aliases.end();
00333                ++alias)
00334             {
00335                   stream << *alias;
00336                   if (alias + 1 != aliases.end())
00337                     stream << ", ";
00338             }
00339           stream << ']';
00340         }
00341       chroot_map::const_iterator is_end(pos);
00342       if ((++is_end) != chroots.end())
00343         stream << ", ";
00344     }
00345 
00346   stream << endl;
00347 }
00348 
00349 void
00350 chroot_config::print_chroot_info (string_list const& chroots,
00351                                   std::ostream&      stream) const
00352 {
00353   for (string_list::const_iterator pos = chroots.begin();
00354        pos != chroots.end();
00355        ++pos)
00356     {
00357       const chroot::ptr chroot = find_alias(*pos);
00358       if (chroot)
00359         {
00360           stream << chroot;
00361           if (pos + 1 != chroots.end())
00362             stream << '\n';
00363         }
00364       else
00365         {
00366           error e(*pos, CHROOT_NOTFOUND);
00367           log_exception_error(e);
00368         }
00369     }
00370 }
00371 
00372 void
00373 chroot_config::print_chroot_location (string_list const& chroots,
00374                                       std::ostream&      stream) const
00375 {
00376   for (string_list::const_iterator pos = chroots.begin();
00377        pos != chroots.end();
00378        ++pos)
00379     {
00380       const chroot::ptr chroot = find_alias(*pos);
00381       if (chroot)
00382         {
00383           stream << chroot->get_path() << '\n';
00384         }
00385       else
00386         {
00387           error e(*pos, CHROOT_NOTFOUND);
00388           log_exception_error(e);
00389         }
00390     }
00391 
00392   stream << std::flush;
00393 }
00394 
00395 void
00396 chroot_config::print_chroot_config (string_list const& chroots,
00397                                     std::ostream&      stream) const
00398 {
00399   keyfile info;
00400 
00401   for (string_list::const_iterator pos = chroots.begin();
00402        pos != chroots.end();
00403        ++pos)
00404     {
00405       const chroot::ptr chroot = find_alias(*pos);
00406 
00407       // Generated chroots (e.g. source chroots) are not printed.
00408       if (chroot)
00409         {
00410           if (chroot->get_original())
00411             info << chroot;
00412         }
00413       else
00414         {
00415           error e(*pos, CHROOT_NOTFOUND);
00416           log_exception_error(e);
00417         }
00418     }
00419 
00420   stream << info;
00421 }
00422 
00423 string_list
00424 chroot_config::validate_chroots (string_list const& chroots) const
00425 {
00426   string_list bad_chroots;
00427 
00428   for (string_list::const_iterator pos = chroots.begin();
00429        pos != chroots.end();
00430        ++pos)
00431     {
00432       const chroot::ptr chroot = find_alias(*pos);
00433       if (!chroot)
00434         bad_chroots.push_back(*pos);
00435     }
00436 
00437   return bad_chroots;
00438 }
00439 
00440 void
00441 chroot_config::load_data (std::string const& file,
00442                           bool               active)
00443 {
00444   log_debug(DEBUG_NOTICE) << "Loading data file: " << file << endl;
00445 
00446   /* Use a UNIX fd, for security (no races) */
00447   int fd = open(file.c_str(), O_RDONLY|O_NOFOLLOW);
00448   if (fd < 0)
00449     throw error(file, FILE_OPEN, strerror(errno));
00450 
00451   // Create a stream buffer from the file descriptor.  The fd will
00452   // be closed when the buffer is destroyed.
00453 #ifdef SCHROOT_FILEBUF_OLD
00454   __gnu_cxx::stdio_filebuf<char> fdbuf(fd, std::ios::in, true, BUFSIZ);
00455 #else
00456   __gnu_cxx::stdio_filebuf<char> fdbuf(fd, std::ios::in);
00457 #endif
00458   std::istream input(&fdbuf);
00459   input.imbue(std::locale::classic());
00460 
00461   sbuild::file_lock lock(fd);
00462   try
00463     {
00464       lock.set_lock(lock::LOCK_SHARED, 2);
00465     }
00466   catch (lock::error const& e)
00467     {
00468       throw error(file, e);
00469     }
00470 
00471   struct stat statbuf;
00472   if (fstat(fd, &statbuf) < 0)
00473     throw error(file, FILE_STAT, strerror(errno));
00474 
00475   if (statbuf.st_uid != 0)
00476     throw error(file, FILE_OWNER);
00477   if (statbuf.st_mode & S_IWOTH)
00478     throw error(file, FILE_PERMS);
00479   if (!S_ISREG(statbuf.st_mode))
00480     throw error(file, FILE_NOTREG);
00481 
00482   try
00483     {
00484       parse_data(input, active);
00485     }
00486   catch (std::runtime_error const& e)
00487     {
00488       throw error(file, e);
00489     }
00490   try
00491     {
00492       lock.unset_lock();
00493     }
00494   catch (lock::error const& e)
00495     {
00496       throw error(file, e);
00497     }
00498 }
00499 
00500 void
00501 chroot_config::parse_data (std::istream& stream,
00502                            bool          active)
00503 {
00504   /* Create key file */
00505   keyfile kconfig(stream);
00506 
00507   load_keyfile(kconfig, active);
00508 }
00509 
00510 void
00511 chroot_config::load_keyfile (keyfile& kconfig,
00512                              bool     active)
00513 {
00514   /* Create chroot objects from key file */
00515   string_list const& groups = kconfig.get_groups();
00516   for (string_list::const_iterator group = groups.begin();
00517        group != groups.end();
00518        ++group)
00519     {
00520       // Set the active property for chroot creation, and create
00521       // the chroot.
00522       kconfig.set_value(*group, "active", active);
00523       std::string type = "plain"; // "plain" is the default type.
00524       kconfig.get_value(*group, "type", type);
00525       chroot::ptr chroot = chroot::create(type);
00526       chroot->set_name(*group);
00527 
00528       kconfig >> chroot;
00529 
00530       add(chroot, kconfig);
00531 
00532       {
00533         chroot_source *source = dynamic_cast<chroot_source *>(chroot.get());
00534         if (source != 0 && !chroot->get_active())
00535           {
00536             chroot::ptr source_chroot = source->clone_source();
00537             if (source_chroot)
00538               add(source_chroot, kconfig);
00539           }
00540       }
00541     }
00542 }

Generated on Sat Jan 27 16:11:03 2007 for schroot by  doxygen 1.5.1