sbuild-run-parts.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-dirstream.h"
00023 #include "sbuild-run-parts.h"
00024 #include "sbuild-util.h"
00025 
00026 #include <cerrno>
00027 
00028 #include <sys/wait.h>
00029 
00030 #include <boost/format.hpp>
00031 #include <boost/regex.hpp>
00032 
00033 using boost::format;
00034 using boost::regex;
00035 using namespace sbuild;
00036 
00037 namespace
00038 {
00039 
00040   typedef std::pair<sbuild::run_parts::error_code,const char *> emap;
00041 
00046   emap init_errors[] =
00047     {
00048       emap(run_parts::CHILD_FORK, N_("Failed to fork child")),
00049       emap(run_parts::CHILD_WAIT, N_("Wait for child failed")),
00050       // TRANSLATORS: %1% = command name
00051       emap(run_parts::EXEC,       N_("Failed to execute '%1%'"))
00052     };
00053 
00054 }
00055 
00056 template<>
00057 error<run_parts::error_code>::map_type
00058 error<run_parts::error_code>::error_strings
00059 (init_errors,
00060  init_errors + (sizeof(init_errors) / sizeof(init_errors[0])));
00061 
00062 run_parts::run_parts (std::string const& directory,
00063                       bool               lsb_mode,
00064                       bool               abort_on_error,
00065                       mode_t             umask):
00066   lsb_mode(true),
00067   abort_on_error(abort_on_error),
00068   umask(umask),
00069   verbose(false),
00070   reverse(false),
00071   directory(directory),
00072   programs()
00073 {
00074   dirstream stream(directory);
00075   direntry de;
00076   while (stream >> de)
00077     {
00078       std::string name(de.name());
00079       if (check_filename(name))
00080         this->programs.insert(name);
00081     }
00082 }
00083 
00084 run_parts::~run_parts ()
00085 {
00086 }
00087 
00088 bool
00089 run_parts::get_verbose () const
00090 {
00091   return this->verbose;
00092 }
00093 
00094 void
00095 run_parts::set_verbose (bool verbose)
00096 {
00097   this->verbose = verbose;
00098 }
00099 
00100 bool
00101 run_parts::get_reverse () const
00102 {
00103   return this->reverse;
00104 }
00105 
00106 void
00107 run_parts::set_reverse (bool reverse)
00108 {
00109   this->reverse = reverse;
00110 }
00111 
00112 int
00113 run_parts::run (string_list const& command,
00114                 environment const& env)
00115 {
00116   int exit_status = 0;
00117 
00118   if (!this->reverse)
00119     {
00120       for (program_set::const_iterator pos = this->programs.begin();
00121            pos != this->programs.end();
00122            ++pos)
00123         {
00124           string_list real_command;
00125           real_command.push_back(*pos);
00126           for (string_list::const_iterator spos = command.begin();
00127                spos != command.end();
00128                ++spos)
00129             real_command.push_back(*spos);
00130 
00131           exit_status = run_child(*pos, real_command, env);
00132 
00133           if (exit_status && this->abort_on_error)
00134             return exit_status;
00135         }
00136     }
00137   else
00138     {
00139       for (program_set::const_reverse_iterator pos = this->programs.rbegin();
00140            pos != this->programs.rend();
00141            ++pos)
00142         {
00143           string_list real_command;
00144           real_command.push_back(*pos);
00145           for (string_list::const_iterator spos = command.begin();
00146                spos != command.end();
00147                ++spos)
00148             real_command.push_back(*spos);
00149 
00150           exit_status = run_child(*pos, real_command, env);
00151 
00152           if (exit_status && this->abort_on_error)
00153             return exit_status;
00154         }
00155     }
00156 
00157   return exit_status;
00158 }
00159 
00160 int
00161 run_parts::run_child (std::string const& file,
00162                       string_list const& command,
00163                       environment const& env)
00164 {
00165   int exit_status = 0;
00166   pid_t pid;
00167 
00168   if ((pid = fork()) == -1)
00169     {
00170       throw error(CHILD_FORK, strerror(errno));
00171     }
00172   else if (pid == 0)
00173     {
00174       try
00175         {
00176           log_debug(DEBUG_INFO) << "run_parts: executing "
00177                                 << string_list_to_string(command, ", ")
00178                                 << std::endl;
00179           if (this->verbose)
00180             // TRANSLATORS: %1% = command
00181             log_info() << format(_("Executing '%1%'"))
00182               % string_list_to_string(command, " ")
00183                        << std::endl;
00184 	  ::umask(this->umask);
00185           exec(this->directory + '/' + file, command, env);
00186           error e(file, EXEC, strerror(errno));
00187           log_exception_error(e);
00188         }
00189       catch (std::exception const& e)
00190         {
00191           sbuild::log_exception_error(e);
00192         }
00193       catch (...)
00194         {
00195           sbuild::log_error()
00196             << _("An unknown exception occurred") << std::endl;
00197         }
00198       _exit(EXIT_FAILURE);
00199     }
00200   else
00201     {
00202       wait_for_child(pid, exit_status);
00203     }
00204 
00205   if (exit_status)
00206     log_debug(DEBUG_INFO) << "run_parts: " << file
00207                           << " failed with status " << exit_status
00208                           << std::endl;
00209   else
00210     log_debug(DEBUG_INFO) << "run_parts: " << file
00211                           << " succeeded"
00212                           << std::endl;
00213 
00214   return exit_status;
00215 }
00216 
00217 void
00218 run_parts::wait_for_child (pid_t pid,
00219                            int&  child_status)
00220 {
00221   child_status = EXIT_FAILURE; // Default exit status
00222 
00223   int status;
00224 
00225   while (1)
00226     {
00227       if (wait(&status) != pid)
00228         {
00229           if (errno == EINTR)
00230             continue; // Wait again.
00231           else
00232             throw error(CHILD_WAIT, strerror(errno));
00233         }
00234       else
00235         break;
00236     }
00237 
00238   if (WIFEXITED(status))
00239     child_status = WEXITSTATUS(status);
00240 }
00241 
00242 bool
00243 run_parts::check_filename (std::string const& name)
00244 {
00245   bool match = false;
00246 
00247   if (this->lsb_mode)
00248     {
00249       static regex lanana_namespace("^[a-z0-9]+$", boost::regex::basic);
00250       static regex lsb_namespace("^_?([a-z0-9_.]+-)+[a-z0-9]+$",
00251                                  boost::regex::basic);
00252       static regex debian_cron_namespace("^[a-z0-9][a-z0-9-]*$",
00253                                          boost::regex::basic);
00254       static regex debian_dpkg_conffile_cruft("dpkg-(old|dist|new|tmp)$",
00255                                               boost::regex::extended);
00256 
00257       if ((regex_match(name, lanana_namespace) ||
00258            regex_match(name, lsb_namespace) ||
00259            regex_match(name, debian_cron_namespace)) &&
00260           !regex_match(name, debian_dpkg_conffile_cruft))
00261         match = true;
00262     }
00263   else
00264     {
00265       static regex traditional_namespace("^[a-zA-Z0-9_-]$",
00266                                          boost::regex::basic);
00267       if (regex_match(name, traditional_namespace))
00268         match = true;
00269     }
00270 
00271   return match;
00272 }

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