git: b64d88424dda - stable/14 - ctld: fix several process setup/teardown bugs

From: Alan Somers <asomers_at_FreeBSD.org>
Date: Thu, 10 Oct 2024 19:16:57 UTC
The branch stable/14 has been updated by asomers:

URL: https://cgit.FreeBSD.org/src/commit/?id=b64d88424dda7785eba815cefcc99709780c2953

commit b64d88424dda7785eba815cefcc99709780c2953
Author:     Alan Somers <asomers@FreeBSD.org>
AuthorDate: 2024-08-07 15:21:08 +0000
Commit:     Alan Somers <asomers@FreeBSD.org>
CommitDate: 2024-10-10 19:11:37 +0000

    ctld: fix several process setup/teardown bugs
    
    All of the below bugs could result in a system where ctld is not
    running, but LUNs and targets still exist in the kernel; a difficult
    situation to recover from.
    
    * open the pidfile earlier.  Open the pidfile before reading the
      kernel's current state, so two racing ctld processes won't step on
      each others' toes.
    
    * close the pidfile later.  Close it after tearing down the
      configuration, for the same reason.
    
    * If the configured pidfile changes, then rename it on SIGHUP rather
      than remove and recreate it.
    
    * When running in debug mode, don't close the pidfile while handling a
      new connection.  Only do that in non-debug mode, in the child of the
      fork.
    
    * Register signal handlers earlier.  Otherwise a SIGTERM signal received
      during startup could kill ctld without tearing down the configuration.
    
    PR:             271460
    Sponsored by:   Axcient
    Reviewed by:    mav
    Pull Request:   https://github.com/freebsd/freebsd-src/pull/1370
    
    (cherry picked from commit 5f89aea7b74aa4605b25af62e31303097a4a48cc)
---
 usr.sbin/ctld/ctld.c | 70 ++++++++++++++++++++++++++++++----------------------
 1 file changed, 40 insertions(+), 30 deletions(-)

diff --git a/usr.sbin/ctld/ctld.c b/usr.sbin/ctld/ctld.c
index fdd3a49f8a48..9be0f6975437 100644
--- a/usr.sbin/ctld/ctld.c
+++ b/usr.sbin/ctld/ctld.c
@@ -1916,7 +1916,6 @@ conf_apply(struct conf *oldconf, struct conf *newconf)
 	struct portal *oldp, *newp;
 	struct port *oldport, *newport, *tmpport;
 	struct isns *oldns, *newns;
-	pid_t otherpid;
 	int changed, cumulated_error = 0, error, sockbuf;
 	int one = 1;
 
@@ -1925,32 +1924,25 @@ conf_apply(struct conf *oldconf, struct conf *newconf)
 		log_init(newconf->conf_debug);
 	}
 
-	if (oldconf->conf_pidfh != NULL) {
-		assert(oldconf->conf_pidfile_path != NULL);
-		if (newconf->conf_pidfile_path != NULL &&
-		    strcmp(oldconf->conf_pidfile_path,
-		    newconf->conf_pidfile_path) == 0) {
-			newconf->conf_pidfh = oldconf->conf_pidfh;
-			oldconf->conf_pidfh = NULL;
-		} else {
-			log_debugx("removing pidfile %s",
-			    oldconf->conf_pidfile_path);
-			pidfile_remove(oldconf->conf_pidfh);
-			oldconf->conf_pidfh = NULL;
-		}
-	}
-
-	if (newconf->conf_pidfh == NULL && newconf->conf_pidfile_path != NULL) {
-		log_debugx("opening pidfile %s", newconf->conf_pidfile_path);
-		newconf->conf_pidfh =
-		    pidfile_open(newconf->conf_pidfile_path, 0600, &otherpid);
-		if (newconf->conf_pidfh == NULL) {
-			if (errno == EEXIST)
-				log_errx(1, "daemon already running, pid: %jd.",
-				    (intmax_t)otherpid);
-			log_err(1, "cannot open or create pidfile \"%s\"",
-			    newconf->conf_pidfile_path);
+	if (oldconf->conf_pidfile_path != NULL &&
+	    newconf->conf_pidfile_path != NULL)
+	{
+		if (strcmp(oldconf->conf_pidfile_path,
+		           newconf->conf_pidfile_path) != 0)
+		{
+			/* pidfile has changed.  rename it */
+			log_debugx("moving pidfile to %s",
+				newconf->conf_pidfile_path);
+			if (rename(oldconf->conf_pidfile_path,
+				   newconf->conf_pidfile_path))
+			{
+				log_err(1, "renaming pidfile %s -> %s",
+					oldconf->conf_pidfile_path,
+					newconf->conf_pidfile_path);
+			}
 		}
+		newconf->conf_pidfh = oldconf->conf_pidfh;
+		oldconf->conf_pidfh = NULL;
 	}
 
 	/*
@@ -2472,8 +2464,8 @@ handle_connection(struct portal *portal, int fd,
 			close(fd);
 			return;
 		}
+		pidfile_close(conf->conf_pidfh);
 	}
-	pidfile_close(conf->conf_pidfh);
 
 	error = getnameinfo(client_sa, client_sa->sa_len,
 	    host, sizeof(host), NULL, 0, NI_NUMERICHOST);
@@ -2808,6 +2800,7 @@ main(int argc, char **argv)
 	struct isns *newns;
 	const char *config_path = DEFAULT_CONFIG_PATH;
 	int debug = 0, ch, error;
+	pid_t otherpid;
 	bool dont_daemonize = false;
 	bool test_config = false;
 	bool use_ucl = false;
@@ -2847,7 +2840,6 @@ main(int argc, char **argv)
 	kernel_init();
 
 	TAILQ_INIT(&kports.pports);
-	oldconf = conf_new_from_kernel(&kports);
 	newconf = conf_new_from_file(config_path, use_ucl);
 
 	if (newconf == NULL)
@@ -2856,6 +2848,22 @@ main(int argc, char **argv)
 	if (test_config)
 		return (0);
 
+	assert(newconf->conf_pidfile_path != NULL);
+	log_debugx("opening pidfile %s", newconf->conf_pidfile_path);
+	newconf->conf_pidfh = pidfile_open(newconf->conf_pidfile_path, 0600,
+		&otherpid);
+	if (newconf->conf_pidfh == NULL) {
+		if (errno == EEXIST)
+			log_errx(1, "daemon already running, pid: %jd.",
+			    (intmax_t)otherpid);
+		log_err(1, "cannot open or create pidfile \"%s\"",
+		    newconf->conf_pidfile_path);
+	}
+
+	register_signals();
+
+	oldconf = conf_new_from_kernel(&kports);
+
 	if (debug > 0) {
 		oldconf->conf_debug = debug;
 		newconf->conf_debug = debug;
@@ -2871,8 +2879,6 @@ main(int argc, char **argv)
 	conf_delete(oldconf);
 	oldconf = NULL;
 
-	register_signals();
-
 	if (dont_daemonize == false) {
 		log_debugx("daemonizing");
 		if (daemon(0, 0) == -1) {
@@ -2927,6 +2933,10 @@ main(int argc, char **argv)
 			error = conf_apply(oldconf, newconf);
 			if (error != 0)
 				log_warnx("failed to apply configuration");
+			if (oldconf->conf_pidfh) {
+				pidfile_remove(oldconf->conf_pidfh);
+				oldconf->conf_pidfh = NULL;
+			}
 			conf_delete(newconf);
 			conf_delete(oldconf);
 			oldconf = NULL;