string command;
int service_type = SVC_PROCESS;
std::list<ServiceRecord *> depends_on;
+ std::list<ServiceRecord *> depends_soft;
string logfile;
// TODO catch I/O exceptions, wrap & re-throw?
string dependency_name = read_setting_value(&i, end);
depends_on.push_back(loadServiceRecord(dependency_name.c_str()));
}
+ else if (setting == "depends-soft") {
+ string dependency_name = read_setting_value(&i, end);
+ depends_soft.push_back(loadServiceRecord(dependency_name.c_str()));
+ }
else if (setting == "logfile") {
logfile = read_setting_value(&i, end);
}
// We've found the dummy record
delete rval;
rval = new ServiceRecord(this, string(name), service_type, command,
- &depends_on);
+ & depends_on, & depends_soft);
rval->setLogfile(logfile);
rval->setAutoRestart(auto_restart);
*iter = rval;
(*i)->start();
}
}
+ for (sr_iter i = soft_dependents.begin(); i != soft_dependents.end(); i++) {
+ if ((*i)->desired_state == SVC_STARTED) {
+ (*i)->start();
+ }
+ }
}
else {
stop();
desired_state = SVC_STOPPED;
service_set->service_inactive(this);
// failure to start
- // TODO - inform listeners of failure
// Cancel start of dependents:
for (sr_iter i = dependents.begin(); i != dependents.end(); i++) {
if ((*i)->desired_state == SVC_STARTED) {
(*i)->failed_dependency();
}
}
+ // What about soft dependents?
+ // TODO we should probably send them "start" rather than "failed_dependency",
+ // or add a parameter to failed_dependency which says whether the dependency
+ // was soft and change start handling as appropriate.
+ // For now just fail the startup:
+ for (sr_iter i = soft_dependents.begin(); i != soft_dependents.end(); i++) {
+ if ((*i)->desired_state == SVC_STARTED) {
+ (*i)->failed_dependency();
+ }
+ }
}
bool ServiceRecord::start_ps_process()
{
- // BIG FAT NOTE: We rely on linux semantics of vfork() here.
- // Specifically:
- // * Parent process execution is suspended until the forked child
- // successfully exec's another program, or it exits
- // * Memory is shared between the two processes until exec()
- // succeeds.
- // Both of the above mean that we can determine in the parent process
- // whether or not the exec succeeded. If vfork instead is implemented
- // as an alias of fork, it will look like the exec always succeeded.
-
- /*
- volatile int exec_status = 0;
- pid_t forkpid = vfork();
- if (forkpid == 0) {
- // Child process
- // ev_default_destroy(); // won't need that on this side, free up fds.
- // Hmm. causes segfault. Of course. Memory is shared due to vfork.
-
- // Re-set stdin, stdout, stderr
- close(0); close(1); close(2);
- string logfile = this->logfile;
- if (logfile.length() == 0) {
- logfile = "/dev/null";
- }
-
- if (open("/dev/null", O_RDONLY) == 0) {
- // stdin = 0. That's what we should have; proceed with opening
- // stdout and stderr.
- open(logfile.c_str(), O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR);
- dup2(1, 2);
- }
-
- const char * pname = program_name.c_str();
- char const * args[2] = { pname, 0 };
- execvp(pname, (char ** const) args);
- // If we got here, the exec failed
- exec_status = errno;
- _exit(0);
- }
- else {
- // Parent process - we only reach here once the exec() above
- // has succeeded, or _exit() above was called (because vfork()
- // suspends the parent until either of those occurs).
- if (exec_status == 0) {
- // success
- pid = forkpid;
-
- // Add a process listener so we can detect when the
- // service stops
- ev_child_init(&child_listener, process_child_callback, pid, 0);
- child_listener.data = this;
- ev_child_start(ev_default_loop(EVFLAG_AUTO), &child_listener);
-
- service_state = SVC_STARTED;
- return true;
- }
- else {
- return false;
- }
- }
- */
-
return start_ps_process(std::vector<std::string>());
}
stop();
for (sr_iter i = dependents.begin(); i != dependents.end(); i++) {
(*i)->forceStop();
- }
+ }
+ // We don't want to force stop soft dependencies, however.
}
// A dependency of this service failed to start.
void ServiceRecord::failed_dependency()
{
- // TODO notify listeners
desired_state = SVC_STOPPED;
// Presumably, we were starting. So now we're not.
if ((*i)->desired_state == SVC_STARTED) {
(*i)->failed_dependency();
}
+ }
+ for (sr_iter i = soft_dependents.begin(); i != soft_dependents.end(); i++) {
+ if ((*i)->desired_state == SVC_STARTED) {
+ (*i)->failed_dependency();
+ }
}
}
void ServiceRecord::dependentStopped()
{
- if (desired_state == SVC_STOPPED || force_stop) {
+ if (service_state != SVC_STOPPED && (desired_state == SVC_STOPPED || force_stop)) {
bool all_deps_stopped = true;
for (sr_iter i = dependents.begin(); i != dependents.end(); ++i) {
if ((*i)->service_state != SVC_STOPPED) {
// and all its dependencies, MUST be stopped.
string program_name; /* executable program or script */
string logfile; /* log file name, empty string specifies /dev/null */
- bool auto_restart; /* whether to restart this (process) if it dies */
+ bool auto_restart; /* whether to restart this (process) if it dies unexpectedly */
typedef std::list<ServiceRecord *> sr_list;
typedef sr_list::iterator sr_iter;
sr_list depends_on; // services this one depends on
sr_list dependents; // services depending on this one
+ sr_list soft_dependents; // services depending on this one via a "soft" dependency
// unsigned wait_count; /* if we are waiting for dependents/dependencies to
// start/stop, this is how many we're waiting for */
}
ServiceRecord(ServiceSet *set, string name, int service_type, string command,
- std::list<ServiceRecord *> * pdepends_on)
+ sr_list * pdepends_on, sr_list * pdepends_soft)
: service_state(SVC_STOPPED), desired_state(SVC_STOPPED), force_stop(false), auto_restart(false)
{
service_set = set;
// TODO splice the contents from the depends_on parameter
// rather than duplicating the list.
this->depends_on = *pdepends_on;
+ this->depends_on.insert(this->depends_on.end(), pdepends_soft->begin(), pdepends_soft->end());
// For each dependency, add us as a dependent.
- for (sr_iter i = depends_on.begin(); i != depends_on.end(); ++i) {
+ for (sr_iter i = pdepends_on->begin(); i != pdepends_on->end(); ++i) {
(*i)->dependents.push_back(this);
}
+ for (sr_iter i = pdepends_soft->begin(); i != pdepends_soft->end(); ++i) {
+ (*i)->soft_dependents.push_back(this);
+ }
}
// Set logfile, should be done before service is started