From b5be811ce6a86fbe8939da5aaa014fa929095a47 Mon Sep 17 00:00:00 2001 From: Victor Moene Date: Wed, 16 Sep 2026 16:20:28 +0200 Subject: [PATCH 1/2] Fixed lmdb maxkeysize assertion for macos Changelog: title Signed-off-by: Victor Moene As of LMDB 1.0.0, MDB_MAXKEYSIZE defaults to 0 (computed from the page size) instead of the fixed compile-time 511 used by LMDB <1.0.0, and mdb_env_get_maxkeysize() only returns the correct value once the environment has actually been opened (env->me_maxkey is populated by mdb_env_open()). So this can only be checked after a successful open, not beforehand like it could with older LMDB versions. The assertion breaks on macos because the environement there uses a newer lmdb package. (cherry picked from commit 0e31cc1dbf58596c83afe4c9cfbad6560d9ca8f0) --- libpromises/dbm_lmdb.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libpromises/dbm_lmdb.c b/libpromises/dbm_lmdb.c index b062774092b..76674c7c0a7 100644 --- a/libpromises/dbm_lmdb.c +++ b/libpromises/dbm_lmdb.c @@ -221,7 +221,6 @@ static int LmdbEnvOpen( { assert(env != NULL); // dereferenced in lmdb (mdb_env_open) assert(path != NULL); // dereferenced (strlen) in lmdb (mdb_env_open) - assert(mdb_env_get_maxkeysize(env) == 511); // Search for 511 in locks.c /* There is a race condition in LMDB that will fail to open the database * environment if another process is opening it at the exact same time. This @@ -233,6 +232,10 @@ static int LmdbEnvOpen( while (attempts-- > 0) { int rc = mdb_env_open(env, path, flags, mode); + if (rc == MDB_SUCCESS) + { + assert(mdb_env_get_maxkeysize(env) >= 511); // Search for 511 in locks.c + } if (rc != ENOENT) { return rc; From 5badc50e2204c595fcf4db7929de72e53767a72d Mon Sep 17 00:00:00 2001 From: Victor Moene Date: Wed, 16 Sep 2026 17:27:14 +0200 Subject: [PATCH 2/2] Fixed flakey macos processes tests Ticket: ENT-14471 Signed-off-by: Victor Moene Changelog: Fixed macOS process start-time/state detection (process_macos.c) and the macOS ps syntax bug in mon_processes_test.c (cherry picked from commit aae13c5e2f7b4b3f1b05134b217c1eb6f29e1de5) --- libpromises/Makefile.am | 7 ++ libpromises/process_macos.c | 111 ++++++++++++++++++++++++++++++++ tests/unit/Makefile.am | 5 +- tests/unit/mon_processes_test.c | 6 ++ 4 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 libpromises/process_macos.c diff --git a/libpromises/Makefile.am b/libpromises/Makefile.am index 88387e12bb8..ba04b3eb9c0 100644 --- a/libpromises/Makefile.am +++ b/libpromises/Makefile.am @@ -205,11 +205,17 @@ libpromises_la_SOURCES += \ process_freebsd.c endif +if MACOSX +libpromises_la_SOURCES += \ + process_macos.c +endif + if !LINUX if !AIX if !HPUX if !SOLARIS if !FREEBSD +if !MACOSX libpromises_la_SOURCES += \ process_unix_stub.c endif @@ -217,6 +223,7 @@ endif endif endif endif +endif endif # !NT diff --git a/libpromises/process_macos.c b/libpromises/process_macos.c new file mode 100644 index 00000000000..d8616d61658 --- /dev/null +++ b/libpromises/process_macos.c @@ -0,0 +1,111 @@ +/* + Copyright 2026 Northern.tech AS + + This file is part of CFEngine 3 - written and maintained by Northern.tech AS. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; version 3. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + + To the extent this program is licensed as part of the Enterprise + versions of CFEngine, the applicable Commercial Open Source License + (COSL) may apply to this file if you as a licensee so wish it. See + included file COSL.txt. +*/ + +#include +#include +#include +#include + +#include +#include + +typedef struct +{ + time_t starttime; + char state; +} ProcessStat; + +/* macOS's kinfo_proc nests the BSD "extern_proc" struct (kp_proc), unlike + * FreeBSD's flattened kinfo_proc, but it still uses the same p_starttime / + * p_stat fields and S* state constants inherited from the shared BSD + * ancestry. */ +static bool GetProcessStat(pid_t pid, ProcessStat *state) +{ + int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid }; + struct kinfo_proc psinfo; + size_t len = sizeof(psinfo); + + /* A successful call with len == 0 means no matching process was found. */ + if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &psinfo, &len, NULL, 0) != 0 || + len == 0) + { + return false; + } + + state->starttime = psinfo.kp_proc.p_starttime.tv_sec; + + switch (psinfo.kp_proc.p_stat) + { + case SRUN: + case SIDL: + state->state = 'R'; + break; + case SSTOP: + state->state = 'T'; + break; + case SSLEEP: + state->state = 'S'; + break; + case SZOMB: + state->state = 'Z'; + break; + default: + state->state = 'X'; + } + return true; +} + +time_t GetProcessStartTime(pid_t pid) +{ + ProcessStat st; + if (GetProcessStat(pid, &st)) + { + return st.starttime; + } + else + { + return PROCESS_START_TIME_UNKNOWN; + } +} + +ProcessState GetProcessState(pid_t pid) +{ + ProcessStat st; + if (GetProcessStat(pid, &st)) + { + switch (st.state) + { + case 'T': + return PROCESS_STATE_STOPPED; + case 'Z': + return PROCESS_STATE_ZOMBIE; + default: + return PROCESS_STATE_RUNNING; + } + } + else + { + return PROCESS_STATE_DOES_NOT_EXIST; + } +} diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index c56dffe6896..28569ed78ce 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -177,9 +177,10 @@ TESTS = $(check_PROGRAMS) $(check_SCRIPTS) if MACOSX XFAIL_TESTS = set_domainname_test -XFAIL_TESTS += process_test -XFAIL_TESTS += mon_processes_test XFAIL_TESTS += rlist_test +# NOTE: cf-monitord's own process-table gathering has a separate, real bug on +# macOS +XFAIL_TESTS += mon_processes_test endif if AIX diff --git a/tests/unit/mon_processes_test.c b/tests/unit/mon_processes_test.c index 50b59fc6205..0a3273dca8d 100644 --- a/tests/unit/mon_processes_test.c +++ b/tests/unit/mon_processes_test.c @@ -48,6 +48,12 @@ static bool GetSysUsers( int *userListSz, int *numRootProcs, int *numOtherProcs) xsnprintf(cbuff, CF_BUFSIZE, "UNIX95=1 /bin/ps -eo user,pid > %s/users.txt", CFWORKDIR); /* SKIP on HP-UX since cf-monitord doesn't count processes correctly! */ return false; +#elif defined(__APPLE__) + /* macOS's BSD ps doesn't understand procps's "keyword:width" column + * sizing syntax used in the generic branch below (it errors out with + * "ps: user:30: keyword not found"). BSD ps doesn't truncate the "user" + * column the way Linux's does, so plain "user,pid" is enough here. */ + xsnprintf(cbuff, CF_BUFSIZE, "ps -eo user,pid > %s/users.txt", CFWORKDIR); #else xsnprintf(cbuff, CF_BUFSIZE, "ps -eo user:30,pid > %s/users.txt", CFWORKDIR); #endif