]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #11457 from grooverdan/sendsigkill_no
authorLennart Poettering <lennart@poettering.net>
Mon, 18 Feb 2019 12:41:52 +0000 (13:41 +0100)
committerGitHub <noreply@github.com>
Mon, 18 Feb 2019 12:41:52 +0000 (13:41 +0100)
service: killmode=cgroup|mixed, SendSIGKILL=no services are not multiprocess

1  2 
man/systemd.kill.xml
src/core/service.c
src/core/unit.c
src/core/unit.h

diff --combined man/systemd.kill.xml
index 1b4a4a84a0dedaaefd839b9bae5bbf36b8f897dc,64e2ea79bf87a37fd0ffb8117c8a3af489a9a62d..09029c56a32dcda8bc0a26df953b146a10e5c4b0
          <constant>SIGKILL</constant> (or the signal specified by
          <varname>FinalKillSignal=</varname>) to remaining processes
          after a timeout, if the normal shutdown procedure left
-         processes of the service around. Takes a boolean value.
-         Defaults to "yes".
+         processes of the service around. When disabled, a
+         <varname>KillMode=</varname> of <constant>control-group</constant>
+         or <constant>mixed</constant> service will not restart if
+         processes from prior services exist within the control group.
+         Takes a boolean value. Defaults to "yes".
          </para></listitem>
        </varlistentry>
  
        <para>
          <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
          <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
 -        <citerefentry><refentrytitle>journalctl</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
 +        <citerefentry><refentrytitle>journalctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
          <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
          <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
          <citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
diff --combined src/core/service.c
index fd9a809b3eba738112f1e93cf9ca0df8246c8c9b,f6cfa1e6bdbbb9a429ea48ee67027bc355607d24..7aa58dd2ca44aca2b1a5c7a5da43a996277bcfa5
@@@ -1458,7 -1458,7 +1458,7 @@@ static int service_spawn
          if (r < 0)
                  return r;
  
 -        our_env = new0(char*, 9);
 +        our_env = new0(char*, 10);
          if (!our_env)
                  return -ENOMEM;
  
                  if (asprintf(our_env + n_env++, "MANAGERPID="PID_FMT, getpid_cached()) < 0)
                          return -ENOMEM;
  
 +        if (s->pid_file)
 +                if (asprintf(our_env + n_env++, "PIDFILE=%s", s->pid_file) < 0)
 +                        return -ENOMEM;
 +
          if (s->socket_fd >= 0) {
                  union sockaddr_union sa;
                  socklen_t salen = sizeof(sa);
@@@ -2010,6 -2006,26 +2010,26 @@@ static void service_kill_control_proces
          }
  }
  
+ static int service_adverse_to_leftover_processes(Service *s) {
+         assert(s);
+         /* KillMode=mixed and control group are used to indicate that all process should be killed off.
+          * SendSIGKILL is used for services that require a clean shutdown. These are typically database
+          * service where a SigKilled process would result in a lengthy recovery and who's shutdown or
+          * startup time is quite variable (so Timeout settings aren't of use).
+          *
+          * Here we take these two factors and refuse to start a service if there are existing processes
+          * within a control group. Databases, while generally having some protection against multiple
+          * instances running, lets not stress the rigor of these. Also ExecStartPre parts of the service
+          * aren't as rigoriously written to protect aganst against multiple use. */
+         if (unit_warn_leftover_processes(UNIT(s)) &&
+             IN_SET(s->kill_context.kill_mode, KILL_MIXED, KILL_CONTROL_GROUP) &&
+             !s->kill_context.send_sigkill) {
+                return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(EBUSY), "Will not start SendSIGKILL=no service of type KillMode=control-group or mixed while processes exist");
+         }
+         return 0;
+ }
  static void service_enter_start(Service *s) {
          ExecCommand *c;
          usec_t timeout;
          service_unwatch_control_pid(s);
          service_unwatch_main_pid(s);
  
-         unit_warn_leftover_processes(UNIT(s));
+         r = service_adverse_to_leftover_processes(s);
+         if (r < 0)
+                 goto fail;
  
          if (s->type == SERVICE_FORKING) {
                  s->control_command_id = SERVICE_EXEC_START;
@@@ -2114,7 -2132,9 +2136,9 @@@ static void service_enter_start_pre(Ser
          s->control_command = s->exec_command[SERVICE_EXEC_START_PRE];
          if (s->control_command) {
  
-                 unit_warn_leftover_processes(UNIT(s));
+                 r = service_adverse_to_leftover_processes(s);
+                 if (r < 0)
+                         goto fail;
  
                  s->control_command_id = SERVICE_EXEC_START_PRE;
  
diff --combined src/core/unit.c
index f4888ff45016392090628d8b90e08819ad3c0c68,a642e0395e55709c21629552b9867485b2edcefd..4e6323d1bdf3a76e13df8e5af2e62eea22d15f1f
@@@ -1895,7 -1895,7 +1895,7 @@@ int unit_reload(Unit *u) 
  
          state = unit_active_state(u);
          if (state == UNIT_RELOADING)
 -                return -EALREADY;
 +                return -EAGAIN;
  
          if (state != UNIT_ACTIVE) {
                  log_unit_warning(u, "Unit cannot be reloaded because it is inactive.");
@@@ -4428,7 -4428,7 +4428,7 @@@ int unit_make_transient(Unit *u) 
          return 0;
  }
  
- static void log_kill(pid_t pid, int sig, void *userdata) {
+ static int log_kill(pid_t pid, int sig, void *userdata) {
          _cleanup_free_ char *comm = NULL;
  
          (void) get_process_comm(pid, &comm);
          /* Don't log about processes marked with brackets, under the assumption that these are temporary processes
             only, like for example systemd's own PAM stub process. */
          if (comm && comm[0] == '(')
-                 return;
+                 return 0;
  
          log_unit_notice(userdata,
                          "Killing process " PID_FMT " (%s) with signal SIG%s.",
                          pid,
                          strna(comm),
                          signal_to_string(sig));
+         return 1;
  }
  
  static int operation_to_signal(KillContext *c, KillOperation k) {
@@@ -5394,29 -5396,31 +5396,31 @@@ int unit_prepare_exec(Unit *u) 
          return 0;
  }
  
- static void log_leftover(pid_t pid, int sig, void *userdata) {
+ static int log_leftover(pid_t pid, int sig, void *userdata) {
          _cleanup_free_ char *comm = NULL;
  
          (void) get_process_comm(pid, &comm);
  
          if (comm && comm[0] == '(') /* Most likely our own helper process (PAM?), ignore */
-                 return;
+                 return 0;
  
          log_unit_warning(userdata,
                           "Found left-over process " PID_FMT " (%s) in control group while starting unit. Ignoring.\n"
                           "This usually indicates unclean termination of a previous run, or service implementation deficiencies.",
                           pid, strna(comm));
+         return 1;
  }
  
void unit_warn_leftover_processes(Unit *u) {
int unit_warn_leftover_processes(Unit *u) {
          assert(u);
  
          (void) unit_pick_cgroup_path(u);
  
          if (!u->cgroup_path)
-                 return;
+                 return 0;
  
-         (void) cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, 0, 0, NULL, log_leftover, u);
+         return cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, 0, 0, NULL, log_leftover, u);
  }
  
  bool unit_needs_console(Unit *u) {
diff --combined src/core/unit.h
index 4d6e6cf46783961660cf3ea6f2cb0d3b38fa2faa,3f9abdf8eeac35e279d30901c9df304b7c9ac002..90d5e6f49db1d89013fe8f1f3f09f7ae08cf1223
@@@ -349,9 -349,6 +349,9 @@@ typedef struct Unit 
          bool exported_log_rate_limit_interval:1;
          bool exported_log_rate_limit_burst:1;
  
 +        /* Whether we warned about clamping the CPU quota period */
 +        bool warned_clamping_cpu_quota_period:1;
 +
          /* When writing transient unit files, stores which section we stored last. If < 0, we didn't write any yet. If
           * == 0 we are in the [Unit] section, if > 0 we are in the unit type-specific section. */
          signed int last_section_private:2;
@@@ -807,7 -804,7 +807,7 @@@ void unit_unlink_state_files(Unit *u)
  
  int unit_prepare_exec(Unit *u);
  
void unit_warn_leftover_processes(Unit *u);
int unit_warn_leftover_processes(Unit *u);
  
  bool unit_needs_console(Unit *u);