* - become a new process group
* - lstat <socket> to find its inum, mtime
* - fork/exec <interp> <script>
- * - periodically lstat <socket> and
- * if inum, mtime have changed
+ * - periodically lstat <interp> and <script> and
+ * if mtime is newer than our start time
* kill process group (at second iteration)
*/
static uid_t us;
static const char *run_base, *script, *socket_path;
-static struct stat sock_stab;
static bool find_run_base_var_run(void) {
struct stat stab;
#endif
}
-static bool check_garbage(void) {
+static bool check_garbage_vs(const struct stat *started) {
struct stat script_stab;
+ struct stat sock_stab;
int r;
r = lstat(script, &script_stab);
if (r) err(127,"lstat script (%s)",script);
- r = lstat(socket_path, &sock_stab);
- if (r) {
- if ((errno == ENOENT))
- return 0; /* well, no garbage then */
- err(127,"stat socket (%s)",socket_path);
- }
-
if (stab_isnewer(&script_stab, &sock_stab))
return 1;
return 0;
}
+static bool check_garbage(void) {
+ struct stat sock_stab;
+ int r;
+
+ r = lstat(socket_path, &sock_stab);
+ if (r) {
+ if ((errno == ENOENT))
+ return 0; /* well, no garbage then */
+ err(127,"stat socket (%s)",socket_path);
+ }
+
+ return check_garbage_vs(&sock_stab);
+}
+
static void tidy_garbage(void) {
/* We lock l<ident> and re-check. The effect of this is that each
* stale socket is removed only once. So unless multiple updates to
} else { /*stage2*/
+ check_baseline_time();
become_pgrp();
- check_socket_baseline();
+ setup_handlers();
spawn_script();
-
- r = lstat(socket_path, &sock_stab);
- if (r) {
- if ((errno == ENOENT))
- return 0; /* well, no garbage then */
- err(127,"stat socket (%s)",socket_path);
+ queue_alarm();
+ await_something();
+ abort();
+
}
+}
+
+/* stage2 */
+/* It is most convenient to handle the recheck timeout, as well as
+ * child death, in signal handlers. Our signals all block each other,
+ * and the main program has signals blocked except in sigsuspend, so
+ * we don't need to worry about async-signal-safety, or errno. */
+static struct stab baseline_time;
+static pid_t script_child, stage2_pgrp;
+static bool out_of_date;
+
+void check_baseline_time(void) {
+#ifdef st_mtime
+ int r = clock_gettime(CLOCK_REALTIME, &baselime_time.st_mtim);
+ if (r) err(127,"(stage2) clock_gettime");
+#else
+ baseline_time.st_mtime = time(NULL);
+ if (baseline_time.st_mtime == (time_t)-1) err(127,"(stage2) time()");
+#endif
+}
+
+static void become_pgrp(void) {
+ int r;
+
+ stage2_pgrp = getpid();
+
+ r = setpgid(0,0);
+ if (r) err(127,"(stage2) setpgid");
+}
+
+static void setup_handlers(void) {
+ struct sigaction sa;
+ int r;
+
+ r = atexit(atexit_handler);
+ if (r) err(127,"(stage2) atexit");
+
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, SIGALRM);
+ sigaddset(&sa.sa_mask, SIGCHLD);
+ sa.sa_flags = 0;
+
+ r = sigprocmask(SIG_BLOCK, &sa.sa_mask, 0);
+ if (r) err(127,"(stage2) sigprocmask(SIG_BLOCK,)");
+
+ sa.sa_handler = alarm_handler;
+ r = sigaction(SIGALRM, &sa, 0);
+ if (r) err(127,"(stage2) sigaction SIGALRM");
+
+ sa.sa_flags |= SA_NOCLDSTOP;
+ sa.sa_handler = child_handler;
+ r = sigaction(SIGCHLD, &sa, 0);
+ if (r) err(127,"(stage2) sigaction SIGCHLD");
+}
+
+static void atexit_handler(void) {
+ int r;
+
+ sighandler_t sigr = signal(SIGTERM,SIG_IGN);
+ if (sigr == SIG_ERR) warn("(stage2) signal(SIGTERM,SIG_IGN)");
+
+ r = killpg(stage2_pgrp,SIGTERM);
+ if (r) warn("(stage) killpg failed");
+}
+
+static void alarm_handler(int dummy) {
+ if (out_of_date)
+ /* second timeout */
+ exit(0); /* transfers control to atexit_handler */
+
+ out_of_date = check_garbage_vs(&baseline_time);
+ queue_alarm();
+}
+
+static void spawn_script(void) {
+ script_child = fork();
+ if (script_child == (pid_t)-1) err(127,"(stage2) fork");
+ if (!script_child) {
+ execlp(interp,
+ interp, script, (char*)0);
+ err(127,"(stage2) exec interpreter (`%s', for `%s')\n",interp,script);
}
}
+
+static void queue_alarm(void) {
+ alarm(check_interval);
+}