File s390-tools-sles15-cpuplugd-Improve-systemctl-start-error-handling.patch of Package s390-tools.10760

Subject: [PATCH] [BZ 161563] cpuplugd: Improve systemctl start error handling
From: Gerald Schaefer <gerald.schaefer@de.ibm.com>

Description:  cpuplugd, mon_tools: Improve systemctl start error handling
Symptom:      "systemctl start cpuplugd/mon_procd/mon_fsstatd" does not
              report any errors in case the startup fails.
Problem:      For type=simple, systemd forks/execs the process and if that
              is successful, it immediately returns. There is no way to
              find out if the initial startup fails.
Solution:     Use type=fork and run the process in the background. In this
              case systemd waits until the initial process returns.
              In addition, use PIDFile and ensure that the pid file is
              already available when the initial process returns. To
              achieve this, use startup synchronization via pipe.
Reproduction: Add invalid values to the config files and start the daemons
              with "systemctl start cpuplugd/mon_procd/mon_fsstatd".
Upstream-ID:  82c8148983fc09e9bfa5bf852b2d39571f8475a0
Problem-ID:   161563

Upstream-Description:

              cpuplugd: Improve systemctl start error handling

              Currently "systemctl start cpuplugd" does not report any errors in
              case the startup fails.

              Example:

               # mv /etc/cpuplugd.conf /etc/cpuplugd.conf.xxx
               # systemctl start cpuplugd

              The reason is that for type=simple systemd forks/execs cpuplugd and if
              that is successful immediately returns. There is no way to find out
              if the initial startup fails.

              Fix this by using type=fork and running cpuplugd in the background. In
              this case systemd waits until the initial process returns.

              In addition use PIDFile and ensure that the pid file is already available
              when the initial cpuplugd process returns. To achieve this, replace the
              daemon() function by our own implementation that introduces startup
              synchronization via pipe. Without that systemd would print the following
              warning:

              systemd[1]: cpuplugd.service: PID file /var/run/cpuplugd.pid not readable
                                            (yet?) after start: No such file or directory

              With this patch, an early startup error like in the example above, is now
              reported correctly in "systemctl start":

               # systemctl start cpuplugd
                 Job for cpuplugd.service failed because the control process exited...
                 See "systemctl status cpuplugd.service" and "journalctl -xe" for ...
               # journalctl -ex | grep cpuplugd
                 Nov 16 15:52:27 ... cpuplugd[5096]: Opening configuration file failed:
                                                     No such file or directory

              Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
              Acked-by: Gerald Schaefer <gerald.schaefer@de.ibm.com>


Signed-off-by: Gerald Schaefer <gerald.schaefer@de.ibm.com>
---
 cpuplugd/cpuplugd.h         |    2 -
 cpuplugd/daemon.c           |   60 ++++++++++++++++++++++++++++++++++++++++++--
 cpuplugd/main.c             |    4 --
 systemd/cpuplugd.service.in |    5 ++-
 4 files changed, 63 insertions(+), 8 deletions(-)

--- a/cpuplugd/cpuplugd.h
+++ b/cpuplugd/cpuplugd.h
@@ -197,10 +197,10 @@ int is_online(int cpuid);
 long get_cmmpages_size();
 void parse_options(int argc, char **argv);
 void check_if_started_twice();
-void store_pid(void);
 void handle_signals(void);
 void handle_sighup(void);
 void reload_daemon(void);
+int daemonize(void);
 int check_cmmfiles(void);
 void check_config();
 void set_cmm_pages(long size);
--- a/cpuplugd/daemon.c
+++ b/cpuplugd/daemon.c
@@ -9,6 +9,10 @@
  * it under the terms of the MIT license. See LICENSE for details.
  */
 
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
 #include "cpuplugd.h"
 
 const char *name = NAME;
@@ -49,7 +53,7 @@ void print_version()
 /*
  * Store daemon's pid so it can be stopped
  */
-void store_pid(void)
+static int store_pid(void)
 {
 	FILE *filp;
 
@@ -57,10 +61,62 @@ void store_pid(void)
 	if (!filp) {
 		cpuplugd_error("cannot open pid file %s: %s\n", pid_file,
 			       strerror(errno));
-		exit(1);
+		return -1;
 	}
 	fprintf(filp, "%d\n", getpid());
 	fclose(filp);
+	return 0;
+}
+
+/*
+ * Run daemon in background and write pid file
+ */
+int daemonize(void)
+{
+	int fd, pipe_fds[2], startup_rc = 1;
+	pid_t pid;
+
+	if (pipe(pipe_fds) == -1) {
+		cpuplugd_error("cannot create pipe\n");
+		return -1;
+	}
+	pid = fork();
+	if (pid < 0)
+		goto close_pipe;
+	if (pid != 0) {
+		/* Wait for startup return code from daemon */
+		if (read(pipe_fds[0], &startup_rc, sizeof(startup_rc)) == -1)
+			cpuplugd_error("cannot read from pipe\n");
+		/* On success daemon has written pid file at this point */
+		exit(startup_rc);
+	}
+	/* Create new session */
+	if (setsid() < 0)
+		goto notify_parent;
+	/* Redirect stdin/out/err to /dev/null */
+	fd = open("/dev/null", O_RDWR, 0);
+	if (fd == -1)
+		goto notify_parent;
+	if (dup2(fd, STDIN_FILENO) < 0)
+		goto notify_parent;
+	if (dup2(fd, STDOUT_FILENO) < 0)
+		goto notify_parent;
+	if (dup2(fd, STDERR_FILENO) < 0)
+		goto notify_parent;
+	/* Create pid file */
+	if (store_pid() < 0)
+		goto notify_parent;
+	startup_rc = 0;
+notify_parent:
+	/* Inform waiting parent about startup return code */
+	if (write(pipe_fds[1], &startup_rc, sizeof(startup_rc)) == -1) {
+		cpuplugd_error("cannot write to pipe\n");
+		startup_rc = 1;
+	}
+close_pipe:
+	close(pipe_fds[0]);
+	close(pipe_fds[1]);
+	return startup_rc ? -1 : 0;
 }
 
 /*
--- a/cpuplugd/main.c
+++ b/cpuplugd/main.c
@@ -418,13 +418,11 @@ int main(int argc, char *argv[])
 	check_config();
 
 	if (!foreground) {
-		rc = daemon(1, 0);
+		rc = daemonize();
 		if (rc < 0)
 			cpuplugd_exit("Detach from terminal failed: %s\n",
 				      strerror(errno));
 	}
-	/* Store daemon pid */
-	store_pid();
 	/* Unlock lock file */
 	flock(fd, LOCK_UN);
 	close(fd);
--- a/systemd/cpuplugd.service.in
+++ b/systemd/cpuplugd.service.in
@@ -13,10 +13,11 @@ Documentation=man:cpuplugd(8) man:cpuplu
 After=remote-fs.target
 
 [Service]
-ExecStart=@usrsbin_path@/cpuplugd -f -c /etc/cpuplugd.conf
+ExecStart=@usrsbin_path@/cpuplugd -c /etc/cpuplugd.conf
 ExecReload=/bin/kill -HUP $MAINPID
 KillMode=process
-Type=simple
+Type=forking
+PIDFile=/var/run/cpuplugd.pid
 
 [Install]
 WantedBy=multi-user.target