errno = ENOSYS;
return -1;
#else
- switch (fork()) {
+ pid_t parent_pid = getpid();
+ pid_t child_pid = fork();
+
+ switch (child_pid) {
case 0:
+ /*
+ * We're in the child process, so we take ownership of
+ * all tempfiles.
+ */
+ reassign_tempfile_ownership(parent_pid, getpid());
break;
case -1:
die_errno(_("fork failed"));
default:
+ /*
+ * We're in the parent process, so we drop ownership of
+ * all tempfiles to prevent us from removing them upon
+ * exit.
+ */
+ reassign_tempfile_ownership(parent_pid, child_pid);
exit(0);
}
if (setsid() == -1)
int path_inside_repo(const char *prefix, const char *path);
void sanitize_stdfds(void);
+
+/*
+ * Daemonize the current process by forking and then exiting the parent
+ * process. Returns 0 when successful, in which case the parent process will
+ * have exited and it's the child process that continues to run the code.
+ * Otherwise, a negative error code is returned and the parent process will
+ * continue execution.
+ *
+ * Note that this function will also perform the following changes:
+ *
+ * - Standard file descriptors in the child process are closed.
+ * - The child process is made a session leader via setsid(3p).
+ * - All tempfiles owned by the parent process are reassigned to the
+ * daemonized child process.
+ */
int daemonize(void);
/*
)
'
+test_expect_success PIPE '--detach holds maintenance lock until daemonized child exits' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+
+ git config maintenance.auto false &&
+ git config core.lockfilepid true &&
+
+ git remote add origin /does/not/exist &&
+ git config set remote.origin.uploadpack "cat fifo-uploadpack" &&
+
+ mkfifo fifo-uploadpack fifo-maint &&
+
+ # Open the maintenance FIFO, as otherwise spawning
+ # git-maintenance(1) would block. Note that we need to open it
+ # as read-write, as otherwise we would block here already.
+ exec 9<>fifo-maint &&
+
+ { git maintenance run --task=prefetch --detach 7>&9 & } &&
+ parent="$!" &&
+
+ # Reap the parent process so that the exec call below will not
+ # get SIGCHLD.
+ wait "$parent" &&
+
+ # Open the git-upload-pack(1) FIFO for writing, which will
+ # block until the upload-pack script opens it for reading. Once
+ # exec returns, we know that the daemonized child is alive and
+ # pinned.
+ exec 8>fifo-uploadpack &&
+
+ test_path_is_file .git/objects/maintenance.lock &&
+ test_path_is_file .git/objects/"maintenance~pid.lock" &&
+
+ # Verify that the maintenance.lock still exists, and
+ # that it was created by the parent process, not the
+ # child.
+ echo "pid $parent" >expect &&
+ test_cmp expect .git/objects/"maintenance~pid.lock" &&
+
+ # Reopen the maintenance FIFO as read-only so that
+ # git-maintenance(1) is the only writer. This will cause it to
+ # close the FIFO once the process exits.
+ exec 9<&- &&
+ exec 9<fifo-maint &&
+
+ # Close the FIFO used by git-upload-pack(1) to unblock it and
+ # then wait until the maintenance FIFO is closed by
+ # git-maintenance(1), indicating that it has exited.
+ exec 8>&- &&
+ cat <&9 &&
+
+ test_path_is_missing .git/objects/maintenance.lock &&
+ test_path_is_missing .git/objects/"maintenance~pid.lock"
+ )
+'
+
test_expect_success '--detach causes maintenance to run in background' '
test_when_finished "rm -rf repo" &&
git init repo &&
return err ? -1 : 0;
}
+
+void reassign_tempfile_ownership(pid_t from, pid_t to)
+{
+ volatile struct volatile_list_head *pos;
+
+ list_for_each(pos, &tempfile_list) {
+ struct tempfile *p = list_entry(pos, struct tempfile, list);
+
+ if (is_tempfile_active(p) && p->owner == from)
+ p->owner = to;
+ }
+}
*/
int rename_tempfile(struct tempfile **tempfile_p, const char *path);
+/*
+ * Reassign ownership of all active tempfiles whose `owner` field matches
+ * `from` to `to`.
+ *
+ * This is intended for use by `daemonize()`; after `fork(2)`-ing, the parent
+ * transfers ownership to the daemonized child so that its atexit handler does
+ * not unlink tempfiles that should outlive it, and the child claims the
+ * inherited tempfiles so that they are cleaned up when the daemon exits.
+ */
+void reassign_tempfile_ownership(pid_t from, pid_t to);
+
#endif /* TEMPFILE_H */