source: trunk/debathena/third/schroot/sbuild/sbuild-chroot-config.cc @ 24314

Revision 24314, 12.9 KB checked in by geofft, 14 years ago (diff)
In schroot: * Merge with Debian unstable; remaining changes: - Backport to Karmic, and adjust build-deps.
Line 
1/* Copyright © 2005-2007  Roger Leigh <rleigh@debian.org>
2 *
3 * schroot is free software: you can redistribute it and/or modify it
4 * under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * schroot is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 * General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program.  If not, see
15 * <http://www.gnu.org/licenses/>.
16 *
17 *********************************************************************/
18
19#include <config.h>
20
21#include "sbuild-chroot.h"
22#include "sbuild-chroot-facet-source-clonable.h"
23#include "sbuild-chroot-config.h"
24#include "sbuild-lock.h"
25
26#include <cassert>
27#include <cerrno>
28#include <cstdlib>
29#include <cstring>
30
31#include <ext/stdio_filebuf.h>
32
33#include <boost/filesystem/operations.hpp>
34
35#include <sys/types.h>
36#include <sys/stat.h>
37#include <fcntl.h>
38
39#include <boost/format.hpp>
40
41using std::endl;
42using boost::format;
43using namespace sbuild;
44
45namespace
46{
47
48  typedef std::pair<chroot_config::error_code,const char *> emap;
49
50  /**
51   * This is a list of the supported error codes.  It's used to
52   * construct the real error codes map.
53   */
54  emap init_errors[] =
55    {
56      // TRANSLATORS: %1% = chroot alias name
57      // TRANSLATORS: %4% = chroot name
58      emap(chroot_config::ALIAS_EXIST,     N_("Alias '%1%' already associated with '%4%' chroot")),
59      emap(chroot_config::CHROOT_NOTFOUND, N_("No such chroot")),
60      // TRANSLATORS: %1% = chroot name
61      emap(chroot_config::CHROOT_EXIST,    N_("A chroot or alias '%1%' already exists with this name")),
62      emap(chroot_config::FILE_NOTREG,     N_("File is not a regular file")),
63      emap(chroot_config::FILE_OPEN,       N_("Failed to open file")),
64      emap(chroot_config::FILE_OWNER,      N_("File is not owned by user root")),
65      emap(chroot_config::FILE_PERMS,      N_("File has write permissions for others"))
66    };
67
68  bool chroot_alphasort (sbuild::chroot::ptr const& c1,
69                         sbuild::chroot::ptr const& c2)
70  {
71    return c1->get_name() < c2->get_name();
72  }
73
74}
75
76template<>
77error<chroot_config::error_code>::map_type
78error<chroot_config::error_code>::error_strings
79(init_errors,
80 init_errors + (sizeof(init_errors) / sizeof(init_errors[0])));
81
82chroot_config::chroot_config ():
83  chroots()
84{
85}
86
87chroot_config::chroot_config (std::string const& file,
88                              bool               active):
89  chroots()
90{
91  add(file, active);
92}
93
94chroot_config::~chroot_config ()
95{
96}
97
98void
99chroot_config::add (std::string const& location,
100                    bool               active)
101{
102  if (stat(location).is_directory())
103    add_config_directory(location, active);
104  else
105    add_config_file(location, active);
106}
107
108void
109chroot_config::add_config_file (std::string const& file,
110                                bool               active)
111{
112  log_debug(DEBUG_NOTICE) << "Loading config file: " << file << endl;
113
114  load_data(file, active);
115}
116
117void
118chroot_config::add_config_directory (std::string const& dir,
119                                     bool               active)
120{
121  log_debug(DEBUG_NOTICE) << "Loading config directory: " << dir << endl;
122
123  if (dir.empty())
124    return;
125
126  boost::filesystem::path dirpath(dir);
127  boost::filesystem::directory_iterator end_iter;
128  for (boost::filesystem::directory_iterator dirent(dirpath);
129       dirent != end_iter;
130       ++dirent)
131    {
132      std::string name(dirent->leaf());
133
134      // Skip common directories.
135      if (name == "." || name == "..")
136        continue;
137
138      // Skip backup files and dpkg configuration backup files.
139      if (!is_valid_filename(name))
140        continue;
141
142      std::string filename = dir + "/" + name;
143
144      try
145        {
146          if (!stat(filename).is_regular())
147            throw error(filename, FILE_NOTREG);
148        }
149      catch (std::runtime_error const& e)
150        {
151          log_exception_warning(e);
152          continue;
153        }
154
155      load_data(filename, active);
156    }
157}
158
159void
160chroot_config::add (chroot::ptr&   chroot,
161                    keyfile const& kconfig)
162{
163  std::string const& name = chroot->get_name();
164
165  // Make sure insertion will succeed.
166  if (this->chroots.find(name) == this->chroots.end() &&
167      this->aliases.find(name) == this->aliases.end())
168    {
169      // Set up chroot.
170      this->chroots.insert(std::make_pair(name, chroot));
171      this->aliases.insert(std::make_pair(name, name));
172
173      // Set up aliases.
174      string_list const& aliases = chroot->get_aliases();
175      for (string_list::const_iterator pos = aliases.begin();
176           pos != aliases.end();
177           ++pos)
178        {
179          try
180            {
181              if (this->aliases.insert(std::make_pair(*pos, name))
182                  .second == false)
183                {
184                  string_map::const_iterator dup = this->aliases.find(*pos);
185                  // Don't warn if alias is for chroot of same name.
186                  if (dup == this->aliases.end() ||
187                      name != dup->first)
188                    {
189                      const char *const key("aliases");
190                      unsigned int line = kconfig.get_line(name, key);
191
192                      if (dup == this->aliases.end())
193                        {
194                          error e(*pos, ALIAS_EXIST);
195                          if (line)
196                            throw keyfile::error(line, name, key,
197                                                 keyfile::PASSTHROUGH_LGK, e);
198                          else
199                            throw keyfile::error(name, key,
200                                                 keyfile::PASSTHROUGH_GK, e);
201                        }
202                      else
203                        {
204                          error e(dup->first, ALIAS_EXIST, dup->second);
205                          if (line)
206                            throw keyfile::error(line, name, key,
207                                                 keyfile::PASSTHROUGH_LGK, e);
208                          else
209                            throw keyfile::error(name, key,
210                                                 keyfile::PASSTHROUGH_GK, e);
211                        }
212                    }
213                }
214            }
215          catch (std::runtime_error const& e)
216            {
217              log_exception_warning(e);
218            }
219        }
220    }
221  else
222    {
223      unsigned int line = kconfig.get_line(name);
224
225      error e(name, CHROOT_EXIST);
226
227      if (line)
228        {
229          keyfile::error ke(line, name, keyfile::PASSTHROUGH_LG, e);
230          ke.set_reason(_("Duplicate names are not allowed"));
231          throw ke;
232        }
233      else
234        {
235          keyfile::error ke(name, keyfile::PASSTHROUGH_G, e);
236          ke.set_reason(_("Duplicate names are not allowed"));
237          throw ke;
238        }
239    }
240}
241
242chroot_config::chroot_list
243chroot_config::get_chroots () const
244{
245  chroot_list ret;
246
247  for (chroot_map::const_iterator pos = this->chroots.begin();
248       pos != this->chroots.end();
249       ++pos)
250    ret.push_back(pos->second);
251
252  std::sort(ret.begin(), ret.end(), chroot_alphasort);
253
254  return ret;
255}
256
257const sbuild::chroot::ptr
258chroot_config::find_chroot (std::string const& name) const
259{
260  chroot_map::const_iterator pos = this->chroots.find(name);
261
262  if (pos != this->chroots.end())
263    return pos->second;
264  else
265    {
266      chroot *null_chroot = 0;
267      return chroot::ptr(null_chroot);
268    }
269}
270
271const sbuild::chroot::ptr
272chroot_config::find_alias (std::string const& name) const
273{
274  string_map::const_iterator pos = this->aliases.find(name);
275
276  if (pos != this->aliases.end())
277    return find_chroot(pos->second);
278  else
279    {
280      chroot *null_chroot = 0;
281      return chroot::ptr(null_chroot);
282    }
283}
284
285string_list
286chroot_config::get_chroot_list () const
287{
288  string_list ret;
289
290  for (string_map::const_iterator pos = this->aliases.begin();
291       pos != this->aliases.end();
292       ++pos)
293    ret.push_back(pos->first);
294
295  std::sort(ret.begin(), ret.end());
296
297  return ret;
298}
299
300void
301chroot_config::print_chroot_list (std::ostream& stream) const
302{
303  string_list chroots = get_chroot_list();
304
305  for (string_list::const_iterator pos = chroots.begin();
306       pos != chroots.end();
307       ++pos)
308    stream << *pos << "\n";
309  stream << std::flush;
310}
311
312void
313chroot_config::print_chroot_list_simple (std::ostream& stream) const
314{
315  stream << _("Available chroots: ");
316
317  for (chroot_map::const_iterator pos = this->chroots.begin();
318       pos != this->chroots.end();
319       ++pos)
320    {
321      stream << pos->second->get_name();
322      string_list const& aliases = pos->second->get_aliases();
323      if (!aliases.empty())
324        {
325          stream << " [";
326          for (string_list::const_iterator alias = aliases.begin();
327               alias != aliases.end();
328               ++alias)
329            {
330                  stream << *alias;
331                  if (alias + 1 != aliases.end())
332                    stream << ", ";
333            }
334          stream << ']';
335        }
336      chroot_map::const_iterator is_end(pos);
337      if ((++is_end) != chroots.end())
338        stream << ", ";
339    }
340
341  stream << endl;
342}
343
344void
345chroot_config::print_chroot_info (string_list const& chroots,
346                                  std::ostream&      stream) const
347{
348  for (string_list::const_iterator pos = chroots.begin();
349       pos != chroots.end();
350       ++pos)
351    {
352      const chroot::ptr chroot = find_alias(*pos);
353      if (chroot)
354        {
355          stream << chroot;
356          if (pos + 1 != chroots.end())
357            stream << '\n';
358        }
359      else
360        {
361          error e(*pos, CHROOT_NOTFOUND);
362          log_exception_error(e);
363        }
364    }
365}
366
367void
368chroot_config::print_chroot_location (string_list const& chroots,
369                                      std::ostream&      stream) const
370{
371  for (string_list::const_iterator pos = chroots.begin();
372       pos != chroots.end();
373       ++pos)
374    {
375      const chroot::ptr chroot = find_alias(*pos);
376      if (chroot)
377        {
378          stream << chroot->get_path() << '\n';
379        }
380      else
381        {
382          error e(*pos, CHROOT_NOTFOUND);
383          log_exception_error(e);
384        }
385    }
386
387  stream << std::flush;
388}
389
390void
391chroot_config::print_chroot_config (string_list const& chroots,
392                                    std::ostream&      stream) const
393{
394  keyfile info;
395
396  for (string_list::const_iterator pos = chroots.begin();
397       pos != chroots.end();
398       ++pos)
399    {
400      const chroot::ptr chroot = find_alias(*pos);
401
402      // Generated chroots (e.g. source chroots) are not printed.
403      if (chroot)
404        {
405          if (chroot->get_original())
406            info << chroot;
407        }
408      else
409        {
410          error e(*pos, CHROOT_NOTFOUND);
411          log_exception_error(e);
412        }
413    }
414
415  stream << info;
416}
417
418string_list
419chroot_config::validate_chroots (string_list const& chroots) const
420{
421  string_list bad_chroots;
422
423  for (string_list::const_iterator pos = chroots.begin();
424       pos != chroots.end();
425       ++pos)
426    {
427      const chroot::ptr chroot = find_alias(*pos);
428      if (!chroot)
429        bad_chroots.push_back(*pos);
430    }
431
432  return bad_chroots;
433}
434
435void
436chroot_config::load_data (std::string const& file,
437                          bool               active)
438{
439  log_debug(DEBUG_NOTICE) << "Loading data file: " << file << endl;
440
441  // stat filename (in case it's a pipe and open(2) blocks)
442  stat file_status1(file);
443  if (file_status1.uid() != 0)
444    throw error(file, FILE_OWNER);
445  if (file_status1.check_mode(stat::PERM_OTHER_WRITE))
446    throw error(file, FILE_PERMS);
447  if (!file_status1.is_regular())
448    throw error(file, FILE_NOTREG);
449
450  /* Use a UNIX fd, for security (no races) */
451  int fd = open(file.c_str(), O_RDONLY);
452  if (fd < 0)
453    throw error(file, FILE_OPEN, strerror(errno));
454
455  // stat fd following open
456  stat file_status2(fd);
457  if (file_status2.uid() != 0)
458    throw error(file, FILE_OWNER);
459  if (file_status2.check_mode(stat::PERM_OTHER_WRITE))
460    throw error(file, FILE_PERMS);
461  if (!file_status2.is_regular())
462    throw error(file, FILE_NOTREG);
463
464  // Create a stream buffer from the file descriptor.  The fd will
465  // be closed when the buffer is destroyed.
466  __gnu_cxx::stdio_filebuf<char> fdbuf(fd, std::ios::in);
467  std::istream input(&fdbuf);
468  input.imbue(std::locale::classic());
469
470  try
471    {
472      sbuild::file_lock lock(fd);
473      lock.set_lock(lock::LOCK_SHARED, 2);
474      parse_data(input, active);
475      lock.unset_lock();
476    }
477  catch (std::runtime_error const& e)
478    {
479      throw error(file, e);
480    }
481}
482
483void
484chroot_config::parse_data (std::istream& stream,
485                           bool          active)
486{
487  /* Create key file */
488  keyfile kconfig(stream);
489
490  load_keyfile(kconfig, active);
491}
492
493void
494chroot_config::load_keyfile (keyfile& kconfig,
495                             bool     active)
496{
497  /* Create chroot objects from key file */
498  string_list const& groups = kconfig.get_groups();
499  for (string_list::const_iterator group = groups.begin();
500       group != groups.end();
501       ++group)
502    {
503      // Set the active property for chroot creation, and create
504      // the chroot.
505      std::string type = "plain"; // "plain" is the default type.
506      kconfig.get_value(*group, "type", type);
507      chroot::ptr chroot = chroot::create(type);
508
509      // Set both; the keyfile load will correct them if needed.
510      chroot->set_name(*group);
511      chroot->set_session_id(*group);
512
513      // If we are (re-)creating session objects, we need to re-clone
514      // the session chroot object from its basic state, in order to
515      // get the correct facets in place.  In the future, it would be
516      // great if sessions could serialise their facet usage to allow
517      // automatic reconstruction.
518      if (active)
519        {
520          chroot = chroot->clone_session("dummy-session-name", "", false);
521          assert(chroot->get_active());
522        }
523      else
524        {
525          assert(!chroot->get_active());
526        }
527
528      chroot->set_name(*group);
529      chroot->set_session_id(*group);
530
531      kconfig >> chroot;
532
533      add(chroot, kconfig);
534
535      {
536        chroot_facet_source_clonable::const_ptr psrc
537          (chroot->get_facet<sbuild::chroot_facet_source_clonable>());
538
539        if (psrc && !chroot->get_active())
540          {
541            chroot::ptr source_chroot = chroot->clone_source();
542            if (source_chroot)
543              add(source_chroot, kconfig);
544          }
545      }
546    }
547}
Note: See TracBrowser for help on using the repository browser.