19 #define PLUGIN_EXIT_VALUE_CHDIR -7
20 #define PLUGIN_EXIT_VALUE_EXEC -8
24 #define PLUGIN_RUNTIME_RESET_FAILURE 3600
27 #define PLUGIN_RUNTIME_MAX_WAIT_TIME 300
52 int res = stat(dir.c_str(), &buf);
53 if(res == -1)
return false;
54 return (buf.st_mode & S_IFDIR) == S_IFDIR;
59 int res = stat(file.c_str(), &buf);
60 if(res == -1)
return false;
61 return (buf.st_mode & S_IFREG) == S_IFREG;
65 return access(file.c_str(), X_OK) == 0;
69 const std::vector<PluginConfig*> &plugins,
70 const std::vector<NetworkConfig*> &networks)
71 : pluginDirectory_(plugindir)
75 std::vector<PluginConfig*>::const_iterator it;
76 for(it = plugins.begin(); it != plugins.end(); ++it) {
78 assert(config->
name.length() > 0);
80 std::vector<PluginState*>::const_iterator sit;
81 for(sit = state_.begin(); sit != state_.end(); ++sit) {
82 if((*sit)->config->name == config->
name) {
83 throw std::runtime_error(
"Multiple plugins exist with name " + config->
name);
87 std::vector<NetworkConfig*>::const_iterator nit;
88 for(nit = networks.begin(); nit != networks.end(); ++nit) {
100 std::vector<PluginState*>::iterator it;
103 for(it = state_.begin(); it != state_.end(); ++it) {
105 stop_plugin(*it,
false);
111 for(it = state_.begin(); it != state_.end(); ++it) {
113 stop_plugin(*it,
true);
119 for(it = state_.begin(); it != state_.end(); ++it) {
120 if((*it)->pid != 0) {
121 std::cerr <<
"In PluginMonitor destructor, failed to stop plugin " << (*it)->config->name << std::endl;
127 void dazeus::PluginMonitor::stop_plugin(
PluginState *state,
bool hard) {
128 assert(state != NULL);
129 assert(state->
pid != 0);
131 assert(config != NULL);
133 int signal = hard ? SIGKILL : SIGINT;
135 if(kill(state->
pid, signal) < 0) {
136 std::cerr <<
"Failed to kill plugin " << config->
name <<
": " << strerror(errno) << std::endl;
141 void dazeus::PluginMonitor::plugin_failed(PluginState *state,
bool permanent) {
143 state->will_autostart =
false;
145 state->num_failures++;
149 bool dazeus::PluginMonitor::start_plugin(PluginState *state) {
150 assert(state != NULL);
151 assert(state->pid == 0);
152 PluginConfig *config = state->
config;
153 assert(config != NULL);
155 state->last_start = time(NULL);
158 std::string path = config->path;
159 if(path.length() == 0) {
166 path = pluginDirectory_ +
"/" + path;
169 std::string executable = config->executable;
170 if(executable.length() == 0) {
172 executable = config->name;
175 std::vector<std::string> arguments;
176 std::string current_arg;
177 bool in_quotes =
false;
178 bool in_doublequotes =
false;
179 bool in_escape =
false;
180 for(
unsigned i = 0; i < config->parameters.length(); ++i) {
181 char c = config->parameters[i];
186 else if(in_quotes || in_doublequotes) {
187 assert(!(in_quotes && in_doublequotes));
188 if((in_quotes && c ==
'\'') || (in_doublequotes && c ==
'"')) {
189 in_quotes = in_doublequotes =
false;
190 arguments.push_back(current_arg);
200 in_doublequotes =
true;
202 else if(isspace(c)) {
203 if(current_arg.length() > 0) {
204 arguments.push_back(current_arg);
209 if(i + 1 == config->parameters.length()) {
212 char d = config->parameters[i+1];
214 case 's': current_arg += socket_->toString(); ++i;
break;
215 case 'n': current_arg += state->network; ++i;
break;
216 default: current_arg +=
'%';
break;
224 if(current_arg.length() > 0) {
225 arguments.push_back(current_arg);
230 std::cerr <<
"Failed to run plugin " << config->name <<
": its directory does not exist" << std::endl;
231 plugin_failed(state);
235 std::string full_executable = executable;
236 if(full_executable[0] !=
'/') {
238 full_executable = path +
'/' + executable;
241 std::cerr <<
"Failed to run plugin " << config->name <<
": its executable does not exist" << std::endl;
242 plugin_failed(state);
245 std::cerr <<
"Failed to run plugin " << config->name <<
": its executable is not executable" << std::endl;
246 plugin_failed(state);
250 pid_t res = fork_plugin(path, arguments, executable);
252 std::cerr <<
"Failed to run plugin " << config->name <<
": " << strerror(errno) << std::endl;
253 plugin_failed(state);
259 std::cout <<
"Plugin " << config->name <<
" started, PID " << state->pid << std::endl;
263 pid_t dazeus::PluginMonitor::fork_plugin(
const std::string path,
const std::vector<std::string> arguments,
const std::string executable) {
271 if(chdir(path.c_str()) < 0) {
276 char **child_argv = (
char**)malloc(arguments.size() *
sizeof(
char*) + 2);
277 child_argv[0] = strdup(executable.c_str());
278 for(
unsigned i = 0; i < arguments.size(); ++i) {
279 child_argv[i + 1] = strdup(arguments[i].c_str());
281 child_argv[arguments.size() + 1] = NULL;
285 execv(executable.c_str(), child_argv);
295 sigset_t signalblock;
296 sigemptyset(&signalblock);
297 sigaddset(&signalblock, SIGCHLD);
298 sigprocmask(SIG_BLOCK, &signalblock, NULL);
303 std::vector<PluginState*>::iterator it;
305 while((child = wait4(-1, &child_status, WNOHANG, NULL))) {
306 if(child == -1 && errno == ECHILD) {
309 }
else if(child == -1) {
310 std::cerr <<
"wait4() returned an error: " << strerror(errno) << std::endl;
315 for(it = state_.begin(); it != state_.end(); ++it) {
316 if((*it)->pid == child) {
322 std::cerr <<
"wait4() returned a PID that is not ours: " << child << std::endl;
328 if(WIFEXITED(child_status)) {
329 std::cerr <<
"Plugin " << state->
config->
name <<
" exited with code " << WEXITSTATUS(child_status) << std::endl;
330 }
else if(WIFSIGNALED(child_status)) {
331 std::string coredumped = WCOREDUMP(child_status) ?
" (core dumped)" :
"";
332 std::cerr <<
"Plugin " << state->
config->
name <<
" killed by signal " << WTERMSIG(child_status) << coredumped << std::endl;
334 std::cerr <<
"Plugin signaled but did not quit? Ignoring..." << std::endl;
349 bool waiting_plugin =
false;
350 for(it = state_.begin(); it != state_.end(); ++it) {
352 assert(state != NULL);
359 if(state->
pid != 0) {
364 int wait_seconds = 0;
372 if(state->
last_start + wait_seconds > time(NULL)) {
375 waiting_plugin =
true;
380 if(!start_plugin(state)) {
381 waiting_plugin =
true;
386 should_run_ = waiting_plugin;
389 sigprocmask(SIG_UNBLOCK, &signalblock, NULL);