From: Davin McCall Date: Thu, 31 Dec 2015 23:50:24 +0000 (+0000) Subject: For scripted services, accept both a "command" and "stop-command" setting. X-Git-Tag: v0.01~77 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=f6ea0c5a334997312b61a434be4adc3866da51b3;p=oweals%2Fdinit.git For scripted services, accept both a "command" and "stop-command" setting. Do not automatically add "start" or "stop" to the command. --- diff --git a/load_service.cc b/load_service.cc index 49bd28f..8957f0e 100644 --- a/load_service.cc +++ b/load_service.cc @@ -187,6 +187,9 @@ ServiceRecord * ServiceSet::loadServiceRecord(const char * name) using std::locale; using std::isspace; + using std::list; + using std::pair; + // First try and find an existing record... ServiceRecord * rval = findService(string(name)); if (rval != 0) { @@ -204,7 +207,9 @@ ServiceRecord * ServiceSet::loadServiceRecord(const char * name) service_filename += name; string command; - std::list> command_offsets; + list> command_offsets; + string stop_command; + list> stop_command_offsets; ServiceType service_type = ServiceType::PROCESS; std::list depends_on; @@ -253,6 +258,9 @@ ServiceRecord * ServiceSet::loadServiceRecord(const char * name) if (setting == "command") { command = read_setting_value(i, end, &command_offsets); } + else if (setting == "stop-command") { + stop_command = read_setting_value(i, end, &stop_command_offsets); + } else if (setting == "depends-on") { string dependency_name = read_setting_value(i, end); depends_on.push_back(loadServiceRecord(dependency_name.c_str())); @@ -330,6 +338,7 @@ ServiceRecord * ServiceSet::loadServiceRecord(const char * name) delete rval; rval = new ServiceRecord(this, string(name), service_type, std::move(command), command_offsets, & depends_on, & depends_soft); + rval->setStopCommand(stop_command, stop_command_offsets); rval->setLogfile(logfile); rval->setAutoRestart(auto_restart); rval->setOnstartFlags(onstart_flags); diff --git a/service.cc b/service.cc index ddb5328..41d04a4 100644 --- a/service.cc +++ b/service.cc @@ -220,7 +220,7 @@ void ServiceRecord::allDepsStarted() noexcept } else if (service_type == ServiceType::SCRIPTED) { // Script-controlled service - bool start_success = start_ps_process(std::vector(1, "start")); + bool start_success = start_ps_process(); if (! start_success) { failed_to_start(); } @@ -291,7 +291,7 @@ void ServiceRecord::failed_to_start() bool ServiceRecord::start_ps_process() noexcept { try { - return start_ps_process(std::vector()); + return start_ps_process(exec_arg_parts); } catch (std::bad_alloc & bad_alloc_exc) { // TODO log error @@ -299,7 +299,7 @@ bool ServiceRecord::start_ps_process() noexcept } } -bool ServiceRecord::start_ps_process(const std::vector &pargs) noexcept +bool ServiceRecord::start_ps_process(const std::vector &cmd) noexcept { // In general, you can't tell whether fork/exec is successful. We use a pipe to communicate // success/failure from the child to the parent. The pipe is set CLOEXEC so a successful @@ -319,89 +319,72 @@ bool ServiceRecord::start_ps_process(const std::vector &pargs) noex } // Set up the argument array and other data now (before fork), in case memory allocation fails. + + auto args = cmd.data(); + + const char * logfile = this->logfile.c_str(); + if (*logfile == 0) { + logfile = "/dev/null"; + } - try { - //auto argsv = std::vector(num_args + pargs.size() + 1); - auto argsv = std::vector(num_args + pargs.size() + 1); - auto args = argsv.data(); - int i; - for (i = 0; i < num_args; i++) { - args[i] = exec_arg_parts[i]; - } - for (auto progarg : pargs) { - args[i] = progarg.c_str(); - i++; - } - args[i] = nullptr; - - string logfile = this->logfile; - if (logfile.length() == 0) { - logfile = "/dev/null"; - } + // TODO make sure pipefd's are not 0/1/2 (STDIN/OUT/ERR) - if they are, dup them + // until they are not. - // TODO make sure pipefd's are not 0/1/2 (STDIN/OUT/ERR) - if they are, dup them - // until they are not. + pid_t forkpid = fork(); + if (forkpid == -1) { + // TODO log error + close(pipefd[0]); + close(pipefd[1]); + return false; + } - pid_t forkpid = fork(); - if (forkpid == -1) { - // TODO log error - close(pipefd[0]); - close(pipefd[1]); - return false; + if (forkpid == 0) { + // Child process. Must not allocate memory (or otherwise risk throwing any exception) + // from here until exit(). + ev_default_destroy(); // won't need that on this side, free up fds. + + // Re-set stdin, stdout, stderr + close(0); close(1); close(2); + + // TODO rethink this logic. If we open it at not-0, shouldn't we just dup it to 0?: + if (open("/dev/null", O_RDONLY) == 0) { + // stdin = 0. That's what we should have; proceed with opening + // stdout and stderr. + open(logfile, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR); + dup2(1, 2); } - if (forkpid == 0) { - // Child process. Must not allocate memory (or otherwise risk throwing any exception) - // from here until exit(). - ev_default_destroy(); // won't need that on this side, free up fds. + execvp(exec_arg_parts[0], const_cast(args)); - // Re-set stdin, stdout, stderr - close(0); close(1); close(2); + // If we got here, the exec failed: + int exec_status = errno; + write(pipefd[1], &exec_status, sizeof(int)); + exit(0); + } + else { + // Parent process + close(pipefd[1]); // close the 'other end' fd - // TODO rethink this logic. If we open it at not-0, shouldn't we just dup it to 0?: - 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); - } + int exec_status; + if (read(pipefd[0], &exec_status, sizeof(int)) == 0) { + // pipe closed; success + pid = forkpid; - execvp(exec_arg_parts[0], const_cast(args)); + // 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); - // If we got here, the exec failed: - int exec_status = errno; - write(pipefd[1], &exec_status, sizeof(int)); - exit(0); + close(pipefd[0]); + return true; } else { - // Parent process - close(pipefd[1]); // close the 'other end' fd - - int exec_status; - if (read(pipefd[0], &exec_status, sizeof(int)) == 0) { - // pipe closed; 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); - - close(pipefd[0]); - return true; - } - else { - // TODO log error - close(pipefd[0]); - return false; - } + // TODO log error + close(pipefd[0]); + return false; } } - catch (std::bad_alloc &bad_alloc_exc) { - log(LogLevel::ERROR, "Out of memory"); - return false; - } } // Mark this and all dependent services as force-stopped. @@ -537,7 +520,7 @@ void ServiceRecord::allDepsStopped() } else if (service_type == ServiceType::SCRIPTED) { // Scripted service. - if (! start_ps_process(std::vector(1, "stop"))) { + if (! start_ps_process(stop_arg_parts)) { // stop script failed, but there's not much we can do: stopped(); } diff --git a/service.h b/service.h index 3704250..ae05e26 100644 --- a/service.h +++ b/service.h @@ -121,12 +121,12 @@ class ServiceDep }; // Given a string and a list of pairs of (start,end) indices for each argument in that string, -// store a null terminator for the argument. Return a `char *` array pointing at the beginning -// of each argument. (The returned array is invalidated if the string is later modified). -static const char ** separate_args(std::string &s, std::list> &arg_indices) +// store a null terminator for the argument. Return a `char *` vector containing the beginning +// of each argument and a trailing nullptr. (The returned array is invalidated if the string is later modified). +static std::vector separate_args(std::string &s, std::list> &arg_indices) { - const char ** r = new const char *[arg_indices.size()]; - int i = 0; + std::vector r; + r.reserve(arg_indices.size() + 1); // First store nul terminator for each part: for (auto index_pair : arg_indices) { @@ -138,9 +138,9 @@ static const char ** separate_args(std::string &s, std::list exec_arg_parts; /* pointer to each argument/part of the program_name */ + + string stop_command; /* storage for stop program/script and arguments */ + std::vector stop_arg_parts; /* pointer to each argument/part of the stop_command */ + OnstartFlags onstart_flags; string logfile; /* log file name, empty string specifies /dev/null */ @@ -219,7 +222,7 @@ class ServiceRecord // For process services, start the process, return true on success bool start_ps_process() noexcept; - bool start_ps_process(const std::vector &args) noexcept; + bool start_ps_process(const std::vector &args) noexcept; // Callback from libev when a child process dies static void process_child_callback(struct ev_loop *loop, struct ev_child *w, @@ -274,7 +277,6 @@ class ServiceRecord program_name = command; exec_arg_parts = separate_args(program_name, command_offsets); - num_args = command_offsets.size(); for (sr_iter i = depends_on.begin(); i != depends_on.end(); ++i) { (*i)->dependents.push_back(this); @@ -291,6 +293,13 @@ class ServiceRecord // TODO write a destructor + // Set the stop command and arguments (may throw std::bad_alloc) + void setStopCommand(std::string command, std::list> &stop_command_offsets) + { + stop_command = command; + stop_arg_parts = separate_args(stop_command, stop_command_offsets); + } + // Get the current service state. ServiceState getState() noexcept {