ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
query.c File Reference

Query tool runtime library implementation. More...

Go to the source code of this file.

Macros

#define HEALTH_CHECK_TIMEOUT_NS   (10000LL * NS_PER_MS_INT)
 
#define HEALTH_CHECK_INTERVAL_NS   (100LL * NS_PER_MS_INT)
 
#define HEALTH_CHECK_CONNECT_TIMEOUT_NS   (500LL * NS_PER_MS_INT)
 

Functions

int query_init (int preferred_port)
 
void query_destroy (void)
 
bool query_is_active (void)
 
int query_get_port (void)
 

Detailed Description

Query tool runtime library implementation.

This provides the QUERY_INIT/QUERY_SHUTDOWN macros that applications use to auto-spawn the query controller process.

Platform support:

  • Unix (macOS/Linux): Uses fork/exec to spawn controller
  • Windows: Uses CreateProcess to spawn controller

Definition in file query.c.

Macro Definition Documentation

◆ HEALTH_CHECK_CONNECT_TIMEOUT_NS

#define HEALTH_CHECK_CONNECT_TIMEOUT_NS   (500LL * NS_PER_MS_INT)

Definition at line 42 of file query.c.

◆ HEALTH_CHECK_INTERVAL_NS

#define HEALTH_CHECK_INTERVAL_NS   (100LL * NS_PER_MS_INT)

Definition at line 41 of file query.c.

◆ HEALTH_CHECK_TIMEOUT_NS

#define HEALTH_CHECK_TIMEOUT_NS   (10000LL * NS_PER_MS_INT)

Definition at line 40 of file query.c.

Function Documentation

◆ query_destroy()

void query_destroy ( void  )

Definition at line 315 of file query.c.

315 {
316 if (!g_query_active && g_controller_pid <= 0) {
317 return;
318 }
319
320 fprintf(stderr, "[query] Shutting down query server...\n");
321
322#ifdef _WIN32
323 if (g_controller_handle != NULL) {
324 // Send termination signal
325 TerminateProcess(g_controller_handle, 0);
326
327 // Wait for process to exit (with timeout)
328 WaitForSingleObject(g_controller_handle, 3000);
329
330 CloseHandle(g_controller_handle);
331 g_controller_handle = NULL;
332 g_controller_pid = 0;
333 }
334
335 // Clean up Winsock (matches WSAStartup in query_init)
336 WSACleanup();
337#else
338 if (g_controller_pid > 0) {
339 // Send SIGTERM for graceful shutdown
340 kill(g_controller_pid, SIGTERM);
341
342 // Wait for process to exit (with timeout)
343 int status;
344 int wait_count = 0;
345 while (wait_count < 30) { // 3 second timeout
346 pid_t result = waitpid(g_controller_pid, &status, WNOHANG);
347 if (result == g_controller_pid) {
348 break;
349 }
350 if (result < 0) {
351 break;
352 }
353 usleep(100 * US_PER_MS_INT); // 100ms
354 wait_count++;
355 }
356
357 // If still running, force kill
358 if (wait_count >= 30) {
359 kill(g_controller_pid, SIGKILL);
360 waitpid(g_controller_pid, &status, 0);
361 }
362
363 g_controller_pid = -1;
364 }
365#endif
366
367 g_query_active = false;
368 g_query_port = -1;
369
370 fprintf(stderr, "[query] Query server stopped\n");
371}

Referenced by query_init().

◆ query_get_port()

int query_get_port ( void  )

Definition at line 405 of file query.c.

405 {
406 return g_query_port;
407}

◆ query_init()

int query_init ( int  preferred_port)

Definition at line 214 of file query.c.

214 {
215 // Already initialized?
216 if (g_query_active) {
217 return g_query_port;
218 }
219
220 // Find the query server executable
221 char server_path[PLATFORM_MAX_PATH_LENGTH];
222 if (!find_query_server_path(server_path, sizeof(server_path))) {
223 fprintf(stderr, "[query] Could not find ascii-query-server executable\n");
224 fprintf(stderr, "[query] Set ASCIICHAT_QUERY_SERVER environment variable or ensure "
225 "it's in .deps-cache/query-tool/\n");
226 return -1;
227 }
228
229#ifdef _WIN32
230 // Windows implementation using CreateProcess
231 WSADATA wsa_data;
232 if (WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0) {
233 fprintf(stderr, "[query] WSAStartup failed\n");
234 return -1;
235 }
236
237 char cmdline[2048];
238 safe_snprintf(cmdline, sizeof(cmdline), "\"%s\" --attach %lu --port %d", server_path,
239 (unsigned long)GetCurrentProcessId(), preferred_port);
240
241 STARTUPINFOA si;
242 PROCESS_INFORMATION pi;
243 memset(&si, 0, sizeof(si));
244 si.cb = sizeof(si);
245 memset(&pi, 0, sizeof(pi));
246
247 // Create the controller process
248 if (!CreateProcessA(NULL, // Use command line
249 cmdline, // Command line
250 NULL, // Process security attributes
251 NULL, // Thread security attributes
252 FALSE, // Don't inherit handles
253 CREATE_NEW_CONSOLE, // Creation flags
254 NULL, // Use parent's environment
255 NULL, // Use parent's directory
256 &si, &pi)) {
257 fprintf(stderr, "[query] Failed to start query server: error %lu\n", GetLastError());
258 return -1;
259 }
260
261 g_controller_handle = pi.hProcess;
262 g_controller_pid = pi.dwProcessId;
263 CloseHandle(pi.hThread); // Don't need the thread handle
264
265 fprintf(stderr, "[query] Started query server (PID %lu) on port %d\n", (unsigned long)g_controller_pid,
266 preferred_port);
267
268#else
269 // Unix implementation using fork/exec
270 pid_t self_pid = getpid();
271
272 char port_str[16];
273 char pid_str[16];
274 safe_snprintf(port_str, sizeof(port_str), "%d", preferred_port);
275 safe_snprintf(pid_str, sizeof(pid_str), "%d", self_pid);
276
277 pid_t child = fork();
278 if (child < 0) {
279 fprintf(stderr, "[query] fork() failed: %s\n", strerror(errno));
280 return -1;
281 }
282
283 if (child == 0) {
284 // Child process: exec the controller
285 // Redirect stdout/stderr to /dev/null or a log file to avoid clutter
286 // (The controller has its own logging)
287
288 execl(server_path, "ascii-query-server", "--attach", pid_str, "--port", port_str, (char *)NULL);
289
290 // If exec fails
291 fprintf(stderr, "[query] exec(%s) failed: %s\n", server_path, strerror(errno));
292 exit(1);
293 }
294
295 // Parent process
296 g_controller_pid = child;
297 fprintf(stderr, "[query] Started query server (PID %d) on port %d\n", child, preferred_port);
298#endif
299
300 // Wait for the HTTP server to become ready
301 fprintf(stderr, "[query] Waiting for HTTP server to be ready...\n");
302 if (!wait_for_http_ready(preferred_port, HEALTH_CHECK_TIMEOUT_NS)) {
303 fprintf(stderr, "[query] Timeout waiting for query server to start\n");
305 return -1;
306 }
307
308 g_query_active = true;
309 g_query_port = preferred_port;
310
311 fprintf(stderr, "[query] Query server ready at http://localhost:%d\n", preferred_port);
312 return preferred_port;
313}
void query_destroy(void)
Definition query.c:315
#define HEALTH_CHECK_TIMEOUT_NS
Definition query.c:40
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.
Definition system.c:456
#define PLATFORM_MAX_PATH_LENGTH
Definition system.c:64

References HEALTH_CHECK_TIMEOUT_NS, PLATFORM_MAX_PATH_LENGTH, query_destroy(), and safe_snprintf().

◆ query_is_active()

bool query_is_active ( void  )

Definition at line 373 of file query.c.

373 {
374 if (!g_query_active) {
375 return false;
376 }
377
378// Verify the controller is still running
379#ifdef _WIN32
380 if (g_controller_handle != NULL) {
381 DWORD exit_code;
382 if (GetExitCodeProcess(g_controller_handle, &exit_code)) {
383 if (exit_code != STILL_ACTIVE) {
384 g_query_active = false;
385 g_controller_handle = NULL;
386 g_controller_pid = 0;
387 return false;
388 }
389 }
390 }
391#else
392 if (g_controller_pid > 0) {
393 // Check if process is still alive
394 if (kill(g_controller_pid, 0) != 0) {
395 g_query_active = false;
396 g_controller_pid = -1;
397 return false;
398 }
399 }
400#endif
401
402 return g_query_active;
403}