#include #include #include #include #include #include #include #include #include #include #include #include #include #include "../include/chroma.h" // Global state pointer for signal handling static chroma_state_t *g_state = NULL; static char *g_config_file = NULL; // Signal handler implementation static void signal_handler_impl(int sig) { switch (sig) { case SIGTERM: case SIGINT: chroma_log("INFO", "Received signal %d (%s), shutting down gracefully", sig, (sig == SIGTERM) ? "SIGTERM" : "SIGINT"); chroma_should_quit = 1; if (g_state) { g_state->running = false; } break; case SIGHUP: chroma_log("INFO", "Received SIGHUP, reloading configuration"); if (g_state && g_config_file) { chroma_reload_config(g_state, g_config_file); } break; case SIGPIPE: // Ignore SIGPIPE - we'll handle broken pipes in read/write calls break; default: chroma_log("WARN", "Received unexpected signal: %d", sig); break; } } // Set up signal handlers void chroma_handle_signals(void) { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = signal_handler_impl; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; // Install signal handlers if (sigaction(SIGTERM, &sa, NULL) == -1) { chroma_log("ERROR", "Failed to install SIGTERM handler: %s", strerror(errno)); } if (sigaction(SIGINT, &sa, NULL) == -1) { chroma_log("ERROR", "Failed to install SIGINT handler: %s", strerror(errno)); } if (sigaction(SIGHUP, &sa, NULL) == -1) { chroma_log("ERROR", "Failed to install SIGHUP handler: %s", strerror(errno)); } // Ignore SIGPIPE sa.sa_handler = SIG_IGN; if (sigaction(SIGPIPE, &sa, NULL) == -1) { chroma_log("ERROR", "Failed to ignore SIGPIPE: %s", strerror(errno)); } chroma_log("DEBUG", "Signal handlers installed"); } // Set global state for signal handling void chroma_set_signal_state(chroma_state_t *state, const char *config_file) { g_state = state; free(g_config_file); g_config_file = config_file ? strdup(config_file) : NULL; } // Clean up signal handling resources void chroma_cleanup_signals(void) { g_state = NULL; free(g_config_file); g_config_file = NULL; } // Expand environment variables in a string static char *expand_env_vars(const char *str) { if (!str || strchr(str, '$') == NULL) { return strdup(str); } char *result = strdup(""); if (!result) { return NULL; } const char *p = str; while (*p) { if (*p == '$') { p++; if (*p == '{') { // ${VAR} format p++; const char *end = strchr(p, '}'); if (!end) { // No closing brace, treat as literal char *tmp = realloc(result, strlen(result) + 2); if (!tmp) { free(result); return NULL; } result = tmp; strcat(result, "${"); break; } size_t var_len = (size_t)(end - p); char *var_name = malloc(var_len + 1); if (!var_name) { free(result); return NULL; } strncpy(var_name, p, var_len); var_name[var_len] = '\0'; const char *var_value = getenv(var_name); if (var_value) { char *tmp = realloc(result, strlen(result) + strlen(var_value) + 1); if (!tmp) { free(var_name); free(result); return NULL; } result = tmp; strcat(result, var_value); } free(var_name); p = end + 1; } else { // $VAR format const char *start = p; while (*p && (isalnum((unsigned char)*p) || *p == '_')) { p++; } if (p == start) { // Not a valid variable name, treat $ as literal char *tmp = realloc(result, strlen(result) + 2); if (!tmp) { free(result); return NULL; } result = tmp; strcat(result, "$"); } else { size_t var_len = (size_t)(p - start); char *var_name = malloc(var_len + 1); if (!var_name) { free(result); return NULL; } strncpy(var_name, start, var_len); var_name[var_len] = '\0'; const char *var_value = getenv(var_name); if (var_value) { char *tmp = realloc(result, strlen(result) + strlen(var_value) + 1); if (!tmp) { free(var_name); free(result); return NULL; } result = tmp; strcat(result, var_value); } free(var_name); } } } else { // Regular character size_t len = strlen(result); char *tmp = realloc(result, len + 2); if (!tmp) { free(result); return NULL; } result = tmp; result[len] = *p; result[len + 1] = '\0'; p++; } } return result; } // Expand tilde and environment variables in path char *chroma_expand_path(const char *path) { if (!path) { return NULL; } // First expand environment variables char *env_expanded = expand_env_vars(path); if (!env_expanded) { return NULL; } // Then expand tilde if present if (env_expanded[0] != '~') { return env_expanded; } const char *home; if (env_expanded[1] == '/' || env_expanded[1] == '\0') { // ~/... or just ~ home = getenv("HOME"); if (!home) { struct passwd *pw = getpwuid(getuid()); if (pw) { home = pw->pw_dir; } } if (!home) { chroma_log("ERROR", "Could not determine home directory"); free(env_expanded); return strdup(path); // Return original path as fallback } size_t home_len = strlen(home); size_t path_len = strlen(env_expanded); char *expanded = malloc(home_len + path_len); // -1 for ~ +1 for \0 if (!expanded) { chroma_log("ERROR", "Failed to allocate memory for path expansion"); free(env_expanded); return strdup(path); } strcpy(expanded, home); if (env_expanded[1] == '/') { strcat(expanded, env_expanded + 1); } free(env_expanded); return expanded; } else { // ~user/... const char *slash = strchr(env_expanded, '/'); size_t user_len = slash ? (size_t)(slash - env_expanded - 1) : strlen(env_expanded) - 1; char *username = malloc(user_len + 1); if (!username) { free(env_expanded); return strdup(path); } strncpy(username, env_expanded + 1, user_len); username[user_len] = '\0'; struct passwd *pw = getpwnam(username); if (!pw) { chroma_log("ERROR", "User not found: %s", username); free(username); free(env_expanded); return strdup(path); } free(username); size_t home_len = strlen(pw->pw_dir); size_t remaining_len = slash ? strlen(slash) : 0; char *expanded = malloc(home_len + remaining_len + 1); if (!expanded) { free(env_expanded); return strdup(path); } strcpy(expanded, pw->pw_dir); if (slash) { strcat(expanded, slash); } free(env_expanded); return expanded; } } // Create directory recursively int chroma_mkdir_recursive(const char *path, mode_t mode) { if (!path) { return -1; } char *path_copy = strdup(path); if (!path_copy) { return -1; } char *p = path_copy; // Skip leading slashes while (*p == '/') { p++; } while (*p) { // Find next slash while (*p && *p != '/') { p++; } if (*p) { *p = '\0'; // Create directory if (mkdir(path_copy, mode) == -1 && errno != EEXIST) { chroma_log("ERROR", "Failed to create directory %s: %s", path_copy, strerror(errno)); free(path_copy); return -1; } *p = '/'; p++; } } // Create final directory if (mkdir(path_copy, mode) == -1 && errno != EEXIST) { chroma_log("ERROR", "Failed to create directory %s: %s", path_copy, strerror(errno)); free(path_copy); return -1; } free(path_copy); return 0; } // Get configuration directory char *chroma_get_config_dir(void) { const char *xdg_config = getenv("XDG_CONFIG_HOME"); if (xdg_config) { char *config_dir = malloc(strlen(xdg_config) + strlen("/chroma") + 1); if (config_dir) { sprintf(config_dir, "%s/chroma", xdg_config); return config_dir; } } const char *home = getenv("HOME"); if (home) { char *config_dir = malloc(strlen(home) + strlen("/.config/chroma") + 1); if (config_dir) { sprintf(config_dir, "%s/.config/chroma", home); return config_dir; } } return strdup("/etc/chroma"); // Fallback } // Check if path exists bool chroma_path_exists(const char *path) { if (!path) { return false; } struct stat st; return (stat(path, &st) == 0); } // Check if path is a regular file bool chroma_is_regular_file(const char *path) { if (!path) { return false; } struct stat st; if (stat(path, &st) != 0) { return false; } return S_ISREG(st.st_mode); } // Check if path is a directory bool chroma_is_directory(const char *path) { if (!path) { return false; } struct stat st; if (stat(path, &st) != 0) { return false; } return S_ISDIR(st.st_mode); } // Get file size long chroma_get_file_size(const char *path) { if (!path) { return -1; } struct stat st; if (stat(path, &st) != 0) { return -1; } return st.st_size; } // Get file extension const char *chroma_get_file_extension(const char *path) { if (!path) { return NULL; } const char *last_dot = strrchr(path, '.'); if (!last_dot || last_dot == path) { return NULL; } return last_dot + 1; } // Get current time in milliseconds long long chroma_get_time_ms(void) { struct timeval tv; if (gettimeofday(&tv, NULL) != 0) { return 0; } return (long long)tv.tv_sec * 1000 + tv.tv_usec / 1000; } // Sleep for specified milliseconds void chroma_sleep_ms(long ms) { if (ms <= 0) { return; } struct timespec ts; ts.tv_sec = ms / 1000; ts.tv_nsec = (ms % 1000) * 1000000; nanosleep(&ts, NULL); } // Format memory size in human readable format void chroma_format_memory_size(size_t bytes, char *buffer, size_t buffer_size) { if (!buffer || buffer_size == 0) { return; } const char *units[] = {"B", "KB", "MB", "GB", "TB"}; const int num_units = sizeof(units) / sizeof(units[0]); double size = (double)bytes; int unit_index = 0; while (size >= 1024.0 && unit_index < num_units - 1) { size /= 1024.0; unit_index++; } if (unit_index == 0) { snprintf(buffer, buffer_size, "%.0f %s", size, units[unit_index]); } else { snprintf(buffer, buffer_size, "%.2f %s", size, units[unit_index]); } } // Cleanup utility functions void chroma_utils_cleanup(void) { chroma_cleanup_signals(); }