#include #include #include #include #include #include #include #include #include #include #include extern char **environ; static const char *PYTHON = "/usr/bin/python3"; static const char *WATCHER = "/Users/ar/bin/daily_activity_watch.py"; static const char *APP_LOG = "/Users/ar/.daily-activity-watch.app.log"; static const char *CHILD_STDOUT = "/Users/ar/.daily-activity-watch.child.log"; static const char *CHILD_STDERR = "/Users/ar/.daily-activity-watch.err.log"; static volatile sig_atomic_t should_stop = 0; static volatile sig_atomic_t child_pid = 0; static void app_log(const char *fmt, ...) { FILE *fh = fopen(APP_LOG, "a"); if (!fh) { return; } time_t now = time(NULL); struct tm local_tm; localtime_r(&now, &local_tm); char ts[32]; strftime(ts, sizeof(ts), "%Y-%m-%d %H:%M:%S", &local_tm); fprintf(fh, "[%s] ", ts); va_list args; va_start(args, fmt); vfprintf(fh, fmt, args); va_end(args); fputc('\n', fh); fclose(fh); } static void handle_signal(int signo) { should_stop = 1; if (child_pid > 0) { kill(child_pid, signo); } } static int spawn_watcher(pid_t *pid) { posix_spawn_file_actions_t actions; posix_spawn_file_actions_init(&actions); posix_spawn_file_actions_addopen(&actions, STDOUT_FILENO, CHILD_STDOUT, O_CREAT | O_APPEND | O_WRONLY, 0644); posix_spawn_file_actions_addopen(&actions, STDERR_FILENO, CHILD_STDERR, O_CREAT | O_APPEND | O_WRONLY, 0644); char *argv[] = {(char *)PYTHON, (char *)WATCHER, NULL}; int rc = posix_spawn(pid, PYTHON, &actions, NULL, argv, environ); posix_spawn_file_actions_destroy(&actions); return rc; } int main(void) { signal(SIGTERM, handle_signal); signal(SIGINT, handle_signal); signal(SIGHUP, handle_signal); app_log("DailyActivityWatch app started"); while (!should_stop) { pid_t pid = 0; int rc = spawn_watcher(&pid); if (rc != 0) { app_log("failed to start watcher: %s", strerror(rc)); sleep(5); continue; } child_pid = pid; app_log("started watcher pid=%d", pid); int status = 0; while (waitpid(pid, &status, 0) < 0) { if (errno == EINTR && should_stop) { break; } if (errno != EINTR) { app_log("waitpid failed: %s", strerror(errno)); break; } } child_pid = 0; if (should_stop) { break; } app_log("watcher exited status=%d; restarting in 5s", status); sleep(5); } app_log("DailyActivityWatch app stopped"); return 0; }