Update.
This commit is contained in:
parent
d7d699059f
commit
304f469a27
146
main.c
146
main.c
@ -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,8 +626,10 @@ int main(void) {
|
|||||||
cleanup_lock:
|
cleanup_lock:
|
||||||
flock(pid_fd, LOCK_UN);
|
flock(pid_fd, LOCK_UN);
|
||||||
close(pid_fd);
|
close(pid_fd);
|
||||||
|
if (access(PID_LOCK_FILE, F_OK) == 0) {
|
||||||
unlink(PID_LOCK_FILE);
|
unlink(PID_LOCK_FILE);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user