From aae13c5e2f7b4b3f1b05134b217c1eb6f29e1de5 Mon Sep 17 00:00:00 2001 From: Victor Moene Date: Wed, 16 Sep 2026 17:27:14 +0200 Subject: [PATCH] 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 --- 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 6411946f666..dc3fcf9dd77 100644 --- a/libpromises/Makefile.am +++ b/libpromises/Makefile.am @@ -206,11 +206,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 @@ -218,6 +224,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 696c34e29c2..44ffa5edb82 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -184,9 +184,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 fff48dd224d..acb6dc122cd 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