diff --git a/main.c b/main.c index 9002e51..bf05284 100644 --- a/main.c +++ b/main.c @@ -25,6 +25,7 @@ #define APP_LOG_FILE "rproc.log" #define PID_LOCK_FILE "/tmp/rproc.pid" #define MAX_LOG_LINE_LEN 4096 +#define TAIL_LINE_COUNT 300 typedef struct Process { pid_t pid; @@ -128,6 +129,11 @@ static void terminate_process(pid_t pid, const char* script_name) { if (kill(pid, SIGTERM) == -1) { if (errno == ESRCH) { app_log("Process %d for '%s' did not exist. Removing from tracking.", pid, script_name); + char pid_filename[FILENAME_MAX]; + snprintf(pid_filename, sizeof(pid_filename), "%s.pid", script_name); + if (access(pid_filename, F_OK) == 0) { + unlink(pid_filename); + } remove_process_by_pid(pid); } return; @@ -138,6 +144,11 @@ static void terminate_process(pid_t pid, const char* script_name) { const pid_t result = waitpid(pid, &status, WNOHANG); if (result == pid) { app_log("Process %d for '%s' terminated gracefully.", pid, script_name); + char pid_filename[FILENAME_MAX]; + snprintf(pid_filename, sizeof(pid_filename), "%s.pid", script_name); + if (access(pid_filename, F_OK) == 0) { + unlink(pid_filename); + } remove_process_by_pid(pid); return; } @@ -153,6 +164,11 @@ static void terminate_process(pid_t pid, const char* script_name) { app_log("Process %d for '%s' disappeared before SIGKILL.", pid, script_name); } waitpid(pid, NULL, 0); + char pid_filename[FILENAME_MAX]; + snprintf(pid_filename, sizeof(pid_filename), "%s.pid", script_name); + if (access(pid_filename, F_OK) == 0) { + unlink(pid_filename); + } remove_process_by_pid(pid); } @@ -214,6 +230,16 @@ static void start_script(const char* script_name) { fprintf(stderr, "Failed to execute script '%s': %s\n", script_name, strerror(errno)); exit(127); } + + char pid_filename[FILENAME_MAX]; + snprintf(pid_filename, sizeof(pid_filename), "%s.pid", script_name); + FILE* pid_fp = fopen(pid_filename, "w"); + if (pid_fp) { + fprintf(pid_fp, "%d", pid); + fclose(pid_fp); + } else { + app_log("Warning: could not create pid file for %s", script_name); + } add_process(pid, script_name); app_log("Started '%s' with PID %d", script_name, pid); @@ -235,16 +261,28 @@ static void handle_sigchld(int sig) { while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { Process* p = find_process_by_pid(pid); if (p) { + char pid_filename[FILENAME_MAX]; + snprintf(pid_filename, sizeof(pid_filename), "%s.pid", p->script_name); + if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { app_log("Script '%s' (PID: %d) crashed with status %d.", p->script_name, pid, WEXITSTATUS(status)); + if (access(pid_filename, F_OK) == 0) { + unlink(pid_filename); + } p->crashed_at = time(NULL); p->pid = -1; } else if (WIFSIGNALED(status)) { app_log("Script '%s' (PID: %d) terminated by signal %d.", p->script_name, pid, WTERMSIG(status)); + if (access(pid_filename, F_OK) == 0) { + unlink(pid_filename); + } p->crashed_at = time(NULL); p->pid = -1; } else { app_log("Script '%s' (PID: %d) exited cleanly.", p->script_name, pid); + if (access(pid_filename, F_OK) == 0) { + unlink(pid_filename); + } remove_process_by_pid(pid); } } @@ -278,7 +316,43 @@ static void setup_signal_handlers(void) { } } +static void adopt_existing_processes(void) { + DIR* d = opendir("."); + if (!d) return; + + struct dirent* dir; + while ((dir = readdir(d)) != NULL) { + size_t len = strlen(dir->d_name); + if (len > 4 && strcmp(dir->d_name + len - 4, ".pid") == 0) { + FILE* fp = fopen(dir->d_name, "r"); + if (!fp) continue; + + pid_t pid = 0; + if (fscanf(fp, "%d", &pid) == 1 && pid > 0) { + fclose(fp); + if (kill(pid, 0) == 0 || errno == EPERM) { + char script_name[FILENAME_MAX]; + strncpy(script_name, dir->d_name, len - 4); + script_name[len - 4] = '\0'; + app_log("Adopting existing process '%s' with PID %d", script_name, pid); + add_process(pid, script_name); + } else { + app_log("Found stale pid file '%s'. Removing.", dir->d_name); + if (access(dir->d_name, F_OK) == 0) { + unlink(dir->d_name); + } + } + } else { + fclose(fp); + } + } + } + closedir(d); +} + static void run_daemon(void) { + adopt_existing_processes(); + DIR* d = opendir("."); if (d) { struct dirent* dir; @@ -363,9 +437,57 @@ static void run_daemon(void) { typedef struct LogFile { char* name; off_t offset; + int color_index; struct LogFile* next; } LogFile; +static off_t get_tail_offset(const char* filename, int line_count) { + FILE* fp = fopen(filename, "rb"); + if (!fp) return 0; + + char buf[4096]; + off_t file_size; + long pos; + int newlines = 0; + + if (fseek(fp, 0, SEEK_END) != 0) { + fclose(fp); + return 0; + } + file_size = ftell(fp); + if (file_size <= 0) { + fclose(fp); + return 0; + } + pos = file_size; + + while (pos > 0) { + long seek_to = pos - sizeof(buf); + if (seek_to < 0) seek_to = 0; + + if (fseek(fp, seek_to, SEEK_SET) != 0) break; + + size_t bytes_read = fread(buf, 1, pos - seek_to, fp); + if (bytes_read == 0) break; + + for (long i = bytes_read - 1; i >= 0; --i) { + if (buf[i] == '\n' && (seek_to + i) != (file_size - 1)) { + newlines++; + if (newlines >= line_count) { + pos = seek_to + i + 1; + fclose(fp); + return pos; + } + } + } + pos = seek_to; + } + + fclose(fp); + return 0; +} + + static void run_monitor(void) { char cwd[PATH_MAX] = {0}; char line_buffer[PATH_MAX]; @@ -386,13 +508,26 @@ static void run_monitor(void) { } printf("Another instance is running in %s. Attaching as a live monitor...\n", cwd); - printf("--- Tailing all *.log files. Press Ctrl+C to exit. ---\n"); + printf("--- Tailing last %d lines from all *.log files. Press Ctrl+C to exit. ---\n", TAIL_LINE_COUNT); LogFile* log_list = NULL; int fd = inotify_init(); if (fd < 0) { perror("inotify_init"); return; } inotify_add_watch(fd, ".", IN_CREATE | IN_MODIFY); + + static const char* colors[] = { + "\033[0;32m", /* Green */ + "\033[0;33m", /* Yellow */ + "\033[0;34m", /* Blue */ + "\033[0;36m", /* Cyan */ + "\033[0;35m", /* Magenta */ + "\033[0;31m", /* Red */ + }; + static const int num_colors = sizeof(colors) / sizeof(colors[0]); + static const char* color_reset = "\033[0m"; + static int next_color_index = 0; + while(1) { DIR* d = opendir("."); @@ -411,7 +546,9 @@ static void run_monitor(void) { if (!found) { LogFile* new_log = malloc(sizeof(LogFile)); new_log->name = strdup(dir->d_name); - new_log->offset = 0; + new_log->offset = get_tail_offset(dir->d_name, TAIL_LINE_COUNT); + new_log->color_index = next_color_index; + next_color_index = (next_color_index + 1) % num_colors; new_log->next = log_list; log_list = new_log; } @@ -426,7 +563,8 @@ static void run_monitor(void) { fseek(fp, l->offset, SEEK_SET); char line[MAX_LOG_LINE_LEN]; while (fgets(line, sizeof(line), fp)) { - printf("[%s] %s", l->name, line); + line[strcspn(line, "\r\n")] = 0; + printf("%s[%s] %s%s\n", colors[l->color_index], l->name, line, color_reset); } l->offset = ftell(fp); fclose(fp); @@ -488,7 +626,9 @@ int main(void) { cleanup_lock: flock(pid_fd, LOCK_UN); close(pid_fd); - unlink(PID_LOCK_FILE); + if (access(PID_LOCK_FILE, F_OK) == 0) { + unlink(PID_LOCK_FILE); + } } return 0; diff --git a/rproc b/rproc index 969a07e..2212ac7 100755 Binary files a/rproc and b/rproc differ