1111
1212// the correct standard headers are included below but clang-tidy doesn't map them.
1313#include <ctype.h>
14+ #ifndef _WIN32
15+ #include <signal.h>
16+ #endif
1417#include "foundation/compat_fs.h"
1518
1619#ifndef CBM_VERSION
@@ -2097,20 +2100,72 @@ static int sha256_file(const char *path, char *out, size_t out_size) {
20972100 return -1 ;
20982101}
20992102
2103+ /* ── Download helper (replaces system("curl ...")) ────────────── */
2104+
2105+ static int cbm_download_to_file (const char * url , const char * dest ) {
2106+ const char * argv [] = {"curl" , "-fSL" , "--progress-bar" , "-o" , dest , url , NULL };
2107+ return cbm_exec_no_shell (argv );
2108+ }
2109+
2110+ static int cbm_download_to_file_quiet (const char * url , const char * dest ) {
2111+ const char * argv [] = {"curl" , "-fsSL" , "-o" , dest , url , NULL };
2112+ return cbm_exec_no_shell (argv );
2113+ }
2114+
2115+ /* ── macOS ad-hoc signing ─────────────────────────────────────── */
2116+
2117+ static int cbm_macos_adhoc_sign (const char * binary_path ) {
2118+ #ifdef __APPLE__
2119+ /* Remove quarantine xattr (best effort — may not exist) */
2120+ const char * xattr_argv [] = {"xattr" , "-d" , "com.apple.quarantine" , binary_path , NULL };
2121+ (void )cbm_exec_no_shell (xattr_argv );
2122+
2123+ /* Ad-hoc sign (required for arm64, harmless for x86_64) */
2124+ const char * sign_argv [] = {"codesign" , "--sign" , "-" , "--force" , binary_path , NULL };
2125+ return cbm_exec_no_shell (sign_argv );
2126+ #else
2127+ (void )binary_path ;
2128+ return 0 ;
2129+ #endif
2130+ }
2131+
2132+ /* ── Kill other MCP server instances ──────────────────────────── */
2133+
2134+ static int cbm_kill_other_instances (void ) {
2135+ #ifdef _WIN32
2136+ const char * argv [] = {"taskkill" , "/IM" , "codebase-memory-mcp.exe" , "/F" , NULL };
2137+ (void )cbm_exec_no_shell (argv );
2138+ return 0 ;
2139+ #else
2140+ int killed = 0 ;
2141+ pid_t self = getpid ();
2142+ FILE * fp = cbm_popen ("pgrep -x codebase-memory-mcp" , "r" );
2143+ if (!fp ) {
2144+ return 0 ;
2145+ }
2146+ char line [32 ];
2147+ while (fgets (line , sizeof (line ), fp )) {
2148+ pid_t pid = (pid_t )strtol (line , NULL , 10 );
2149+ if (pid > 0 && pid != self ) {
2150+ if (kill (pid , SIGTERM ) == 0 ) {
2151+ killed ++ ;
2152+ }
2153+ }
2154+ }
2155+ cbm_pclose (fp );
2156+ return killed ;
2157+ #endif
2158+ }
2159+
21002160/* Download checksums.txt and verify the archive integrity.
21012161 * Returns: 0 = verified OK, 1 = mismatch (FAIL), -1 = could not verify (warning). */
21022162static int verify_download_checksum (const char * archive_path , const char * archive_name ) {
21032163 char checksum_file [256 ];
21042164 snprintf (checksum_file , sizeof (checksum_file ), "%s/cbm-checksums.txt" , cbm_tmpdir ());
21052165
2106- char cmd [1024 ];
2107- snprintf (cmd , sizeof (cmd ),
2108- "curl -fsSL -o '%s' "
2109- "'https://github.com/DeusData/codebase-memory-mcp/releases/latest/download/"
2110- "checksums.txt' 2>/dev/null" ,
2111- checksum_file );
2112- // NOLINTNEXTLINE(cert-env33-c) — intentional CLI subprocess for download
2113- int rc = system (cmd );
2166+ int rc = cbm_download_to_file_quiet (
2167+ "https://github.com/DeusData/codebase-memory-mcp/releases/latest/download/checksums.txt" ,
2168+ checksum_file );
21142169 if (rc != 0 ) {
21152170 fprintf (stderr , "warning: could not download checksums.txt — skipping verification\n" );
21162171 cbm_unlink (checksum_file );
@@ -2840,10 +2895,7 @@ int cbm_cmd_update(int argc, char **argv) {
28402895 char tmp_archive [256 ];
28412896 snprintf (tmp_archive , sizeof (tmp_archive ), "%s/cbm-update.%s" , cbm_tmpdir (), ext );
28422897
2843- char cmd [1024 ];
2844- snprintf (cmd , sizeof (cmd ), "curl -fSL --progress-bar -o '%s' '%s'" , tmp_archive , url );
2845- // NOLINTNEXTLINE(cert-env33-c) — intentional CLI subprocess for download
2846- int rc = system (cmd );
2898+ int rc = cbm_download_to_file (url , tmp_archive );
28472899 if (rc != 0 ) {
28482900 fprintf (stderr , "error: download failed (exit %d)\n" , rc );
28492901 cbm_unlink (tmp_archive );
@@ -2870,6 +2922,14 @@ int cbm_cmd_update(int argc, char **argv) {
28702922 /* crc == -1: could not verify (warning only), crc == 0: verified OK */
28712923 }
28722924
2925+ /* Step 4c: Kill running MCP server instances before replacement */
2926+ {
2927+ int killed = cbm_kill_other_instances ();
2928+ if (killed > 0 ) {
2929+ printf ("Stopped %d running MCP server instance(s).\n" , killed );
2930+ }
2931+ }
2932+
28732933 /* Step 5: Extract binary */
28742934 char bin_dest [1024 ];
28752935 snprintf (bin_dest , sizeof (bin_dest ), "%s/.local/bin/codebase-memory-mcp" , home );
@@ -2936,6 +2996,13 @@ int cbm_cmd_update(int argc, char **argv) {
29362996 }
29372997 }
29382998
2999+ /* Step 5b: macOS ad-hoc signing (required for arm64, harmless for x86_64) */
3000+ #ifdef __APPLE__
3001+ if (cbm_macos_adhoc_sign (bin_dest ) != 0 ) {
3002+ fprintf (stderr , "warning: ad-hoc signing failed — binary may not run on macOS arm64\n" );
3003+ }
3004+ #endif
3005+
29393006 /* Step 6: Reinstall skills (force to pick up new content) */
29403007 char skills_dir [1024 ];
29413008 snprintf (skills_dir , sizeof (skills_dir ), "%s/.claude/skills" , home );
0 commit comments