From 5f0b280dabaade739901468a2a8a9672e827ca95 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 28 May 2026 14:57:24 -0400 Subject: [PATCH 1/6] Low: tools: Handle UTF-8 parameters in cibsecret. We need to set LC_ALL before calling g_option_context_parse_strv so glib knows how to handle UTF-8 parameters. Otherwise, we'll get an invalid byte sequence error. This is not surrounded by #ifdef ENABLE_NLS since it should be safe to do anyway. Really, we should probably be doing this early in every command line tool. If NLS is enabled, we'll do this in crm_log_preinit but that's too late - that function gets called by crm_log_init, which is called by pcmk__cli_init_logging, which is typically called after command line parameters are parsed (because we need to know how many -Vs were given). Thus, it needs to be done separately and earlier. However in the interests of not introducing too many changes this late in a release, I'm only making this change to the one tool that has a bug filed against it. This only started being a problem once cibsecret was rewritten in C. Fixes RHEL-178864 --- tools/cibsecret.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/cibsecret.c b/tools/cibsecret.c index 1bc0df5d8e0..37ad9e7fdc3 100644 --- a/tools/cibsecret.c +++ b/tools/cibsecret.c @@ -10,6 +10,7 @@ #include #include // EINVAL, ENODEV, ENOENT, ENOTCONN +#include // setlocale #include #include #include // setenv, unsetenv @@ -1104,6 +1105,9 @@ main(int argc, char **argv) rsh_fn_t rsh_fn; rcp_fn_t rcp_fn; + // Load locale information for the local host from the environment + setlocale(LC_ALL, ""); + pcmk__register_formats(output_group, formats); if (!g_option_context_parse_strv(context, &processed_args, &error)) { exit_code = CRM_EX_USAGE; From 78e32ebdae97f3153bb9a48552ef23b532050265 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 28 May 2026 16:38:47 -0400 Subject: [PATCH 2/6] Low: tools: Handle UTF-8 parameters in crm_resource. Because cibsecret calls crm_resource in various places, it also needs to call setlocale. It's unclear to me why this needs to be done now instead of always. I don't see anything that's changed in crm_resource, and the old bash-based cibsecret also called crm_resource. Still, this should be safe to call from this tool as well. --- tools/crm_resource.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/crm_resource.c b/tools/crm_resource.c index 8e655d1f6e3..486c8f81634 100644 --- a/tools/crm_resource.c +++ b/tools/crm_resource.c @@ -25,6 +25,7 @@ #include #include #include +#include // setlocale #include #include // xmlXPathObject, etc. @@ -2064,6 +2065,9 @@ main(int argc, char **argv) processed_args = pcmk__cmdline_preproc(argv, "GHINSTdginpstuvx"); context = build_arg_context(args, &output_group); + // Load locale information for the local host from the environment + setlocale(LC_ALL, ""); + pcmk__register_formats(output_group, formats); if (!g_option_context_parse_strv(context, &processed_args, &error)) { exit_code = CRM_EX_USAGE; From b2254ced92447cd22ed57ee7ad802a76f0697fc7 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 28 May 2026 15:04:59 -0400 Subject: [PATCH 3/6] Refactor: cts: Add a UTF-8 character to the cibsecret test. --- python/pacemaker/_cts/tests/cibsecret.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/pacemaker/_cts/tests/cibsecret.py b/python/pacemaker/_cts/tests/cibsecret.py index 7be2951321a..f8d5a807887 100644 --- a/python/pacemaker/_cts/tests/cibsecret.py +++ b/python/pacemaker/_cts/tests/cibsecret.py @@ -33,7 +33,7 @@ def __init__(self, cm, env): self.name = "Cibsecret" self._secret = "passwd" - self._secret_val = "SecreT_PASS" + self._secret_val = "SečreT_PASS" self._rid = "secretDummy" self._startall = SimulStartLite(cm, env) From 74d48269553cbf974cdeeabc1c4c312c25d562eb Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Fri, 29 May 2026 11:02:43 -0400 Subject: [PATCH 4/6] Low: tools: Quote certain cmdline arguments in cibsecret. When cibsecret was written in shell, it was quoting command line arguments like this. I'm not certain it's all strictly necessary (for instance, are characters that would need to be quoted valid in resource IDs?) it's best to follow what we were previously doing and be safe. At the least, it's possible for the values of secrets to have spaces in them which would require quoting. --- tools/cibsecret.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tools/cibsecret.c b/tools/cibsecret.c index 37ad9e7fdc3..b5ec22b55c2 100644 --- a/tools/cibsecret.c +++ b/tools/cibsecret.c @@ -436,7 +436,7 @@ sync_one_file(pcmk__output_t *out, rsh_fn_t rsh_fn, rcp_fn_t rcp_fn, dirname = g_path_get_dirname(path); - cmdline = pcmk__assert_asprintf("mkdir -p %s", dirname); + cmdline = pcmk__assert_asprintf("mkdir -p '%s'", dirname); rc = rsh_fn(out, peers, cmdline); if (rc != pcmk_rc_ok) { goto done; @@ -456,7 +456,7 @@ sync_one_file(pcmk__output_t *out, rsh_fn_t rsh_fn, rcp_fn_t rcp_fn, } else { free(cmdline); - cmdline = pcmk__assert_asprintf("rm -f %s %s.sign", path, path); + cmdline = pcmk__assert_asprintf("rm -f '%s' '%s.sign'", path, path); rc = rsh_fn(out, peers, cmdline); } @@ -478,7 +478,7 @@ check_cib_rsc(pcmk__output_t *out, const char *rsc) return rc; } - cmdline = pcmk__assert_asprintf("crm_resource -r %s -W", rsc); + cmdline = pcmk__assert_asprintf("crm_resource -r '%s' -W", rsc); rc = run_cmdline(out, cmdline, NULL); free(cmdline); @@ -512,7 +512,7 @@ get_cib_param(pcmk__output_t *out, const char *rsc, const char *param) return NULL; } - cmdline = pcmk__assert_asprintf("crm_resource -r %s -g %s --output-as=xml", + cmdline = pcmk__assert_asprintf("crm_resource -r '%s' -g '%s' --output-as=xml", rsc, param); rc = run_cmdline(out, cmdline, &standard_out); @@ -559,7 +559,7 @@ remove_cib_param(pcmk__output_t *out, const char *rsc, const char *param) return rc; } - cmdline = pcmk__assert_asprintf("crm_resource -r %s -d %s", rsc, param); + cmdline = pcmk__assert_asprintf("crm_resource -r '%s' -d '%s'", rsc, param); rc = run_cmdline(out, cmdline, NULL); free(cmdline); return rc; @@ -576,7 +576,7 @@ set_cib_param(pcmk__output_t *out, const char *rsc, const char *param, return rc; } - cmdline = pcmk__assert_asprintf("crm_resource -r %s -p %s -v %s", rsc, + cmdline = pcmk__assert_asprintf("crm_resource -r '%s' -p '%s' -v '%s'", rsc, param, value); rc = run_cmdline(out, cmdline, NULL); free(cmdline); @@ -630,7 +630,7 @@ local_files_remove(pcmk__output_t *out, rsh_fn_t rsh_fn, rcp_fn_t rcp_fn, lf_file = pcmk__assert_asprintf(PCMK__CIB_SECRETS_DIR "/%s/%s", rsc, param); - cmdline = pcmk__assert_asprintf("rm -f %s %s.sign", lf_file, lf_file); + cmdline = pcmk__assert_asprintf("rm -f '%s' '%s.sign'", lf_file, lf_file); rc = run_cmdline(out, cmdline, NULL); if (rc == pcmk_rc_ok) { From 6a8f96a7ba8486c445c82a01fe571eda86ed37b0 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Fri, 29 May 2026 11:05:18 -0400 Subject: [PATCH 5/6] Refactor: cts: Quote command line arguments to crm_resource. --- python/pacemaker/_cts/tests/cibsecret.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/python/pacemaker/_cts/tests/cibsecret.py b/python/pacemaker/_cts/tests/cibsecret.py index f8d5a807887..64f89516595 100644 --- a/python/pacemaker/_cts/tests/cibsecret.py +++ b/python/pacemaker/_cts/tests/cibsecret.py @@ -79,7 +79,7 @@ def _remove_dummy(self, node): def _check_cib_value(self, node, expected): """Check that the secret has the expected value.""" - (rc, lines) = self._rsh.call(node, f"crm_resource -r {self._rid} -g {self._secret}", + (rc, lines) = self._rsh.call(node, f"crm_resource -r '{self._rid}' -g '{self._secret}'", verbose=1) s = " ".join(lines).strip() @@ -91,7 +91,7 @@ def _check_cib_value(self, node, expected): def _test_check(self, node): """Test the 'cibsecret check' subcommand.""" - (rc, _) = self._rsh.call(node, f"cibsecret check {self._rid} {self._secret}", + (rc, _) = self._rsh.call(node, f"cibsecret check '{self._rid}' '{self._secret}'", verbose=1) if rc != 0: return self.failure("Failed to check secret") @@ -101,7 +101,7 @@ def _test_check(self, node): def _test_delete(self, node): """Test the 'cibsecret delete' subcommand.""" - (rc, _) = self._rsh.call(node, f"cibsecret delete {self._rid} {self._secret}", + (rc, _) = self._rsh.call(node, f"cibsecret delete '{self._rid}' '{self._secret}'", verbose=1) if rc != 0: return self.failure("Failed to delete secret") @@ -111,7 +111,7 @@ def _test_delete(self, node): def _test_get(self, node, expected): """Test the 'cibsecret get' subcommand.""" - (rc, lines) = self._rsh.call(node, f"cibsecret get {self._rid} {self._secret}", + (rc, lines) = self._rsh.call(node, f"cibsecret get '{self._rid}' '{self._secret}'", verbose=1) s = " ".join(lines).strip() @@ -123,7 +123,7 @@ def _test_get(self, node, expected): def _test_set(self, node): """Test the 'cibsecret set' subcommand.""" - (rc, _) = self._rsh.call(node, f"cibsecret set {self._rid} {self._secret} {self._secret_val}", + (rc, _) = self._rsh.call(node, f"cibsecret set '{self._rid}' '{self._secret}' '{self._secret_val}'", verbose=1) if rc != 0: return self.failure("Failed to set secret") @@ -133,7 +133,7 @@ def _test_set(self, node): def _test_stash(self, node): """Test the 'cibsecret stash' subcommand.""" - (rc, _) = self._rsh.call(node, f"cibsecret stash {self._rid} {self._secret}", + (rc, _) = self._rsh.call(node, f"cibsecret stash '{self._rid}' '{self._secret}'", verbose=1) if rc != 0: return self.failure(f"Failed to stash secret {self._secret}") @@ -152,7 +152,7 @@ def _test_sync(self, node): def _test_unstash(self, node): """Test the 'cibsecret unstash' subcommand.""" - (rc, _) = self._rsh.call(node, f"cibsecret unstash {self._rid} {self._secret}", + (rc, _) = self._rsh.call(node, f"cibsecret unstash '{self._rid}' '{self._secret}'", verbose=1) if rc != 0: return self.failure(f"Failed to unstash secret {self._secret}") From 8fd7ef350bf171c30a04dd037442b72aaa9cb4ef Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Fri, 29 May 2026 11:05:45 -0400 Subject: [PATCH 6/6] Refactor: cts: Add a space character to the cibsecret test. --- python/pacemaker/_cts/tests/cibsecret.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/pacemaker/_cts/tests/cibsecret.py b/python/pacemaker/_cts/tests/cibsecret.py index 64f89516595..6e61f0b56cb 100644 --- a/python/pacemaker/_cts/tests/cibsecret.py +++ b/python/pacemaker/_cts/tests/cibsecret.py @@ -33,7 +33,7 @@ def __init__(self, cm, env): self.name = "Cibsecret" self._secret = "passwd" - self._secret_val = "SečreT_PASS" + self._secret_val = "SečreT PASS" self._rid = "secretDummy" self._startall = SimulStartLite(cm, env)