This commit is contained in:
retoor 2025-09-28 01:46:51 +02:00
parent d7d699059f
commit 304f469a27
2 changed files with 144 additions and 4 deletions

148
main.c
View File

@ -25,6 +25,7 @@
#define APP_LOG_FILE "rproc.log" #define APP_LOG_FILE "rproc.log"
#define PID_LOCK_FILE "/tmp/rproc.pid" #define PID_LOCK_FILE "/tmp/rproc.pid"
#define MAX_LOG_LINE_LEN 4096 #define MAX_LOG_LINE_LEN 4096
#define TAIL_LINE_COUNT 300
typedef struct Process { typedef struct Process {
pid_t pid; pid_t pid;
@ -128,6 +129,11 @@ static void terminate_process(pid_t pid, const char* script_name) {
if (kill(pid, SIGTERM) == -1) { if (kill(pid, SIGTERM) == -1) {
if (errno == ESRCH) { if (errno == ESRCH) {
app_log("Process %d for '%s' did not exist. Removing from tracking.", pid, script_name); 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); remove_process_by_pid(pid);
} }
return; return;
@ -138,6 +144,11 @@ static void terminate_process(pid_t pid, const char* script_name) {
const pid_t result = waitpid(pid, &status, WNOHANG); const pid_t result = waitpid(pid, &status, WNOHANG);
if (result == pid) { if (result == pid) {
app_log("Process %d for '%s' terminated gracefully.", pid, script_name); 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); remove_process_by_pid(pid);
return; 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); app_log("Process %d for '%s' disappeared before SIGKILL.", pid, script_name);
} }
waitpid(pid, NULL, 0); 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); remove_process_by_pid(pid);
} }
@ -215,6 +231,16 @@ static void start_script(const char* script_name) {
exit(127); 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); add_process(pid, script_name);
app_log("Started '%s' with PID %d", script_name, pid); 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) { while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
Process* p = find_process_by_pid(pid); Process* p = find_process_by_pid(pid);
if (p) { if (p) {
char pid_filename[FILENAME_MAX];
snprintf(pid_filename, sizeof(pid_filename), "%s.pid", p->script_name);
if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
app_log("Script '%s' (PID: %d) crashed with status %d.", p->script_name, pid, WEXITSTATUS(status)); 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->crashed_at = time(NULL);
p->pid = -1; p->pid = -1;
} else if (WIFSIGNALED(status)) { } else if (WIFSIGNALED(status)) {
app_log("Script '%s' (PID: %d) terminated by signal %d.", p->script_name, pid, WTERMSIG(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->crashed_at = time(NULL);
p->pid = -1; p->pid = -1;
} else { } else {
app_log("Script '%s' (PID: %d) exited cleanly.", p->script_name, pid); 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); 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) { static void run_daemon(void) {
adopt_existing_processes();
DIR* d = opendir("."); DIR* d = opendir(".");
if (d) { if (d) {
struct dirent* dir; struct dirent* dir;
@ -363,9 +437,57 @@ static void run_daemon(void) {
typedef struct LogFile { typedef struct LogFile {
char* name; char* name;
off_t offset; off_t offset;
int color_index;
struct LogFile* next; struct LogFile* next;
} LogFile; } 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) { static void run_monitor(void) {
char cwd[PATH_MAX] = {0}; char cwd[PATH_MAX] = {0};
char line_buffer[PATH_MAX]; char line_buffer[PATH_MAX];
@ -386,7 +508,7 @@ static void run_monitor(void) {
} }
printf("Another instance is running in %s. Attaching as a live monitor...\n", cwd); 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; LogFile* log_list = NULL;
int fd = inotify_init(); int fd = inotify_init();
@ -394,6 +516,19 @@ static void run_monitor(void) {
inotify_add_watch(fd, ".", IN_CREATE | IN_MODIFY); 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) { while(1) {
DIR* d = opendir("."); DIR* d = opendir(".");
if (d) { if (d) {
@ -411,7 +546,9 @@ static void run_monitor(void) {
if (!found) { if (!found) {
LogFile* new_log = malloc(sizeof(LogFile)); LogFile* new_log = malloc(sizeof(LogFile));
new_log->name = strdup(dir->d_name); 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; new_log->next = log_list;
log_list = new_log; log_list = new_log;
} }
@ -426,7 +563,8 @@ static void run_monitor(void) {
fseek(fp, l->offset, SEEK_SET); fseek(fp, l->offset, SEEK_SET);
char line[MAX_LOG_LINE_LEN]; char line[MAX_LOG_LINE_LEN];
while (fgets(line, sizeof(line), fp)) { 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); l->offset = ftell(fp);
fclose(fp); fclose(fp);
@ -488,7 +626,9 @@ int main(void) {
cleanup_lock: cleanup_lock:
flock(pid_fd, LOCK_UN); flock(pid_fd, LOCK_UN);
close(pid_fd); close(pid_fd);
unlink(PID_LOCK_FILE); if (access(PID_LOCK_FILE, F_OK) == 0) {
unlink(PID_LOCK_FILE);
}
} }
return 0; return 0;

BIN
rproc

Binary file not shown.