FUSE Daemonization API
======================

This document describes the FUSE daemonization APIs, including the legacy
fuse_daemonize() function and the newfuse_daemonize_early_start()/signal() API
introduced in libfuse 3.19.


Overview
--------

FUSE filesystems often need to run as background daemons. Daemonization
involves forking the process, creating a new session, and redirecting
standard file descriptors. The challenge is properly reporting initialization
failures to the parent process.


Old API: fuse_daemonize()
--------------------------

Function signature:
    int fuse_daemonize(int foreground);

Location: lib/helper.c

This is the legacy daemonization API, primarily used with the high-level
fuse_main() interface.

Behavior:
- If foreground=0: forks the process, creates a new Unix session,
  redirects stdio
- If foreground=1: only changes directory to "/"
- Parent waits for a single byte on a pipe before exiting
- Child writes completion byte immediately after redirecting stdio
- Always changes directory to "/"

Limitations:
1. No failure reporting: The parent receives notification immediately after
   fork/setsid, before any meaningful initialization (like mounting the
   filesystem or starting threads).

2. Timing constraint: Must be called AFTER fuse_session_mount() in existing
   examples, because there's no way to report mount failures to the parent.

3. Thread initialization: Cannot report failures from complex initialization
   steps like:
   - Starting worker threads
   - Network connection setup
   - RDMA memory registration
   - Resource allocation

4. FUSE_SYNC_INIT incompatibility: With the FUSE_SYNC_INIT feature, FUSE_INIT
   happens at mount time and may start io_uring threads. This requires
   daemonization BEFORE mount, which the old API cannot handle properly.

Example usage (old API):
    fuse = fuse_new(...);
    fuse_mount(fuse, mountpoint);
    fuse_daemonize(opts.foreground);  // After mount, can't report mount failure
    fuse_set_signal_handlers(se);
    fuse_session_loop(se);


New API: fuse_daemonize_early_start() /fuse_daemonize_early_success() /fuse_daemonize_early_fail()
--------------------------------------------------------------------------------------------------

Functions:
    int fuse_daemonize_early_start(unsigned int flags);
    void fuse_daemonize_early_success(void);
    void fuse_daemonize_early_fail(void);
    bool fuse_daemonize_early_active(void);

Location: lib/fuse_daemonize.c, include/fuse_daemonize.h
Available since: libfuse 3.19

This new API solves the limitations of fuse_daemonize() by splitting
daemonization into two phases:

1.   fuse_daemonize_early_start()        - Fork and setup, but parent waits
2.1. fuse_daemonize_early_fail()         - Signal failure to parent
2.2  fuse_daemonize_early_success()      - Signal startup to success to the parent
     See below for an important detail.


fuse_daemonize_early_start()
----------------------------

Flags:
- FUSE_DAEMONIZE_NO_CHDIR: Don't change directory to "/"
- FUSE_DAEMONIZE_NO_BACKGROUND: Don't fork (foreground mode)

Behavior:
- Unless NO_BACKGROUND: forks the process
- Parent waits for status signal from child
- Child creates new session and continues
- Unless NO_CHDIR: changes directory to "/"
- Closes stdin immediately in child
- Starts a watcher thread to detect parent death
- Returns 0 in child on success, negative errno on error

Parent death detection:
- Uses a "death pipe" - parent keeps write end open
- Child's watcher thread polls the read end
- If parent dies, pipe gets POLLHUP and child exits
- Prevents orphaned daemons if parent is killed

Child death detection:
- The parent will detect a pipe failure for the
  read(signal_pipe[0], ...) and exit with an error.

fuse_daemonize_early_fail(int err)
----------------------------------

Behavior:
- Signals the parent about the provided error
- Parent will exit with that error

fuse_daemonize_early_success()
-------------------------------
- Signals the parent process with success
- On success: redirects stdout/stderr to /dev/null
- Stops the parent watcher thread
- Cleans up pipes and internal state
- Safe to call multiple times
- Safe to call even if fuse_daemonize_early_start() failed
- Needs to be called after successful mount

Example usage (new API):
-------------------------

    // Start daemonization BEFORE mount
    unsigned int daemon_flags = 0;
    if (foreground)
        daemon_flags |= FUSE_DAEMONIZE_NO_BACKGROUND;

    if (fuse_daemonize_start(daemon_flags) != 0)
        goto error;

    // Complex initialization can fail and be reported
    if (setup_threads() != 0)
        goto error_signal;

    if (setup_network() != 0)
        goto error_signal;
    // Mount can now fail and be reported to parent
    if (fuse_session_mount(se, mountpoint) != 0)
        goto error_signal;

    // Signal success - parent exits with EXIT_SUCCESS
    // This is typically done in the init() callback after FUSE_INIT
   fuse_daemonize_early_signal(FUSE_DAEMONIZE_SUCCESS);

    // Run main loop
    fuse_session_loop(se);

    return 0;

error_signal:
    // Signal failure - parent exits with EXIT_FAILURE
   fuse_daemonize_early_signal(FUSE_DAEMONIZE_FAILURE);
error:
    return 1;


