git: 7dfadd78ba3c - main - hastd: Add a stop control message

From: Dag-Erling Smørgrav <des_at_FreeBSD.org>
Date: Sat, 05 Sep 2026 15:44:45 UTC
The branch main has been updated by des:

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

commit 7dfadd78ba3c4608c3631fb5008de77ea1e29e88
Author:     Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2026-09-05 15:44:09 +0000
Commit:     Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2026-09-05 15:44:35 +0000

    hastd: Add a stop control message
    
    Add a stop control message which causes hastd to clean up and terminate.
    
    Reviewed by:    kevans
    Differential Revision:  https://reviews.freebsd.org/D59344
---
 sbin/hastctl/hastctl.c | 14 ++++++++++++++
 sbin/hastd/control.c   | 17 +++++++++++++++++
 sbin/hastd/hast.h      |  1 +
 sbin/hastd/hastd.c     | 12 ++++++++----
 4 files changed, 40 insertions(+), 4 deletions(-)

diff --git a/sbin/hastctl/hastctl.c b/sbin/hastctl/hastctl.c
index fe37dcaa1b58..229af1dae74d 100644
--- a/sbin/hastctl/hastctl.c
+++ b/sbin/hastctl/hastctl.c
@@ -59,6 +59,7 @@ enum {
 	CMD_ROLE,
 	CMD_STATUS,
 	CMD_DUMP,
+	CMD_STOP,
 	CMD_LIST
 };
 
@@ -430,6 +431,9 @@ main(int argc, char *argv[])
 	} else if (strcmp(argv[1], "dump") == 0) {
 		cmd = CMD_DUMP;
 		optstr = "c:dh";
+	} else if (strcmp(argv[1], "stop") == 0) {
+		cmd = CMD_STOP;
+		optstr = "c:dh";
 	} else
 		usage();
 
@@ -524,6 +528,13 @@ main(int argc, char *argv[])
 				nv_add_string(nv, argv[ii], "resource%d", ii);
 		}
 		break;
+	case CMD_STOP:
+		/* Stop hastd. */
+		if (argc > 0)
+			usage();
+		nv = nv_alloc();
+		nv_add_uint8(nv, HASTCTL_CMD_STOP, "cmd");
+		break;
 	default:
 		PJDLOG_ABORT("Impossible command!");
 	}
@@ -574,6 +585,9 @@ main(int argc, char *argv[])
 	case CMD_STATUS:
 		error = control_status(nv);
 		break;
+	case CMD_STOP:
+		error = 0;
+		break;
 	default:
 		PJDLOG_ABORT("Impossible command!");
 	}
diff --git a/sbin/hastd/control.c b/sbin/hastd/control.c
index 0a9befcc4358..7b13cc71f2c9 100644
--- a/sbin/hastd/control.c
+++ b/sbin/hastd/control.c
@@ -300,6 +300,15 @@ control_status(struct hastd_config *cfg, struct nv *nvout,
 	control_status_worker(res, nvout, no);
 }
 
+static void
+control_stop(struct hastd_config *cfg, struct nv *nvout)
+{
+	pjdlog_info("Stop message received.");
+	(void)cfg;
+	(void)nvout;
+	sigexit_received = true;
+}
+
 void
 control_handle(struct hastd_config *cfg)
 {
@@ -341,6 +350,14 @@ control_handle(struct hastd_config *cfg)
 
 	error = 0;
 
+	/* The stop command does not expect any arguments. */
+	if (cmd == HASTCTL_CMD_STOP) {
+		control_stop(cfg, nvout);
+		if (nv_error(nvout) != 0)
+			goto close;
+		goto fail;
+	}
+
 	str = nv_get_string(nvin, "resource0");
 	if (str == NULL) {
 		pjdlog_error("Control header is missing 'resource0' field.");
diff --git a/sbin/hastd/hast.h b/sbin/hastd/hast.h
index d0f9dd874c56..a91993fc398e 100644
--- a/sbin/hastd/hast.h
+++ b/sbin/hastd/hast.h
@@ -66,6 +66,7 @@
 #define	HASTCTL_CMD_UNKNOWN	0
 #define	HASTCTL_CMD_SETROLE	1
 #define	HASTCTL_CMD_STATUS	2
+#define	HASTCTL_CMD_STOP	3
 
 #define	HAST_ROLE_UNDEF		0
 #define	HAST_ROLE_INIT		1
diff --git a/sbin/hastd/hastd.c b/sbin/hastd/hastd.c
index c62b85fd1925..5d90ddc8aff9 100644
--- a/sbin/hastd/hastd.c
+++ b/sbin/hastd/hastd.c
@@ -724,7 +724,6 @@ terminate_workers(void)
 {
 	struct hast_resource *res;
 
-	pjdlog_info("Termination signal received, exiting.");
 	TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) {
 		if (res->hr_workerpid == 0)
 			continue;
@@ -1044,10 +1043,8 @@ check_signals(void)
 		switch (signo) {
 		case SIGINT:
 		case SIGTERM:
+			pjdlog_info("Termination signal received.");
 			sigexit_received = true;
-			terminate_workers();
-			proto_close(cfg->hc_controlconn);
-			exit(EX_OK);
 			break;
 		case SIGCHLD:
 			child_exit();
@@ -1077,6 +1074,8 @@ main_loop(void)
 
 	for (;;) {
 		check_signals();
+		if (sigexit_received)
+			break;
 
 		/* Setup descriptors for select(2). */
 		FD_ZERO(&rfds);
@@ -1135,6 +1134,8 @@ main_loop(void)
 		 * info about terminated workers in the meantime.
 		 */
 		check_signals();
+		if (sigexit_received)
+			break;
 
 		if (FD_ISSET(proto_descriptor(cfg->hc_controlconn), &rfds))
 			control_handle(cfg);
@@ -1170,6 +1171,9 @@ main_loop(void)
 			}
 		}
 	}
+	terminate_workers();
+	proto_close(cfg->hc_controlconn);
+	exit(EX_OK);
 }
 
 static void