00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
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
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
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;
00222
00223 int status;
00224
00225 while (1)
00226 {
00227 if (wait(&status) != pid)
00228 {
00229 if (errno == EINTR)
00230 continue;
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 }