--- # On Linux `multica daemon autostart enable` writes a systemd *user* unit. # A user unit does not start at boot unless the user lingers — without this the # daemon only comes back when someone logs in, which on a headless agent host # is never. - name: Daemon | Enable systemd lingering for the daemon user ansible.builtin.command: argv: ["loginctl", "enable-linger", "{{ multica_user }}"] creates: "/var/lib/systemd/linger/{{ multica_user }}" become: true when: - multica_enable_linger | bool - multica_autostart | bool - name: Daemon | Wait for the user's runtime directory ansible.builtin.wait_for: path: "/run/user/{{ _multica_uid }}" state: present timeout: 30 become: true when: - multica_enable_linger | bool - multica_autostart | bool - name: Daemon | Read the current autostart registration ansible.builtin.command: argv: "{{ _multica_cmd + ['daemon', 'autostart', 'status'] }}" become: true become_user: "{{ multica_user }}" environment: "{{ _multica_user_env }}" register: _multica_autostart_status changed_when: false failed_when: false check_mode: false - name: Daemon | Register boot autostart ansible.builtin.command: argv: "{{ _multica_cmd + ['daemon', 'autostart', 'enable'] }}" become: true become_user: "{{ multica_user }}" environment: "{{ _multica_user_env }}" when: - multica_autostart | bool - _multica_autostart_status.rc != 0 or 'enabled' not in (_multica_autostart_status.stdout | default('') | lower) register: _multica_autostart_enable changed_when: true # Lifecycle goes through systemd, never `multica daemon start|restart`. Those # fork the daemon inside the CALLER's session (here, Ansible's SSH session # scope), outside the unit's cgroup: the unit stays inactive, nothing # supervises the process, and the next `systemctl --user start` collides with # it on daemon.lock. Seen on haunted-mansion (2026-10-21) and spaceship-earth # (2026-10-22). # # The unit name comes from `autostart status` ("Location: .../.service") # so a --profile host resolves its own unit. - name: Daemon | Re-read the autostart registration ansible.builtin.command: argv: "{{ _multica_cmd + ['daemon', 'autostart', 'status'] }}" become: true become_user: "{{ multica_user }}" environment: "{{ _multica_user_env }}" register: _multica_autostart_reread changed_when: false failed_when: false check_mode: false when: _multica_autostart_enable is changed - name: Daemon | Resolve the systemd user unit ansible.builtin.set_fact: _multica_unit: >- {{ ((_s.stdout | default('')) ~ '\n' ~ (_s.stderr | default(''))) | regex_search('Location:\s*\S*/([^/\s]+\.service)', '\1') | default(['multica-daemon.service'], true) | first }} vars: _s: "{{ _multica_autostart_reread if _multica_autostart_enable is changed else _multica_autostart_status }}" # `autostart enable` may rewrite the unit file while the user manager still # holds the old definition. - name: Daemon | Reload the user manager after a (re)registration ansible.builtin.command: argv: [systemctl, --user, daemon-reload] become: true become_user: "{{ multica_user }}" environment: "{{ _multica_user_env }}" when: _multica_autostart_enable is changed changed_when: true - name: Daemon | Read the unit state ansible.builtin.command: argv: [systemctl, --user, is-active, "{{ _multica_unit }}"] become: true become_user: "{{ multica_user }}" environment: "{{ _multica_user_env }}" register: _multica_unit_state changed_when: false failed_when: false check_mode: false - name: Daemon | Read daemon status ansible.builtin.command: argv: "{{ _multica_cmd + ['daemon', 'status', '--output', 'json'] }}" become: true become_user: "{{ multica_user }}" environment: "{{ _multica_user_env }}" register: _multica_daemon_status changed_when: false failed_when: false check_mode: false # Running, but not under the unit: an orphan. Stop it so the unit can own the # lock and the process. - name: Daemon | Stop a daemon running outside its unit ansible.builtin.command: argv: "{{ _multica_cmd + ['daemon', 'stop'] }}" become: true become_user: "{{ multica_user }}" environment: "{{ _multica_user_env }}" when: - (_multica_unit_state.stdout | trim) != 'active' - _multica_daemon_status.rc == 0 - ((_multica_daemon_status.stdout | from_json).status | default('')) == 'running' changed_when: true - name: Daemon | Start the unit if it is not active ansible.builtin.command: argv: [systemctl, --user, start, "{{ _multica_unit }}"] become: true become_user: "{{ multica_user }}" environment: "{{ _multica_user_env }}" when: (_multica_unit_state.stdout | trim) != 'active' changed_when: true # Anything that changed the binary, the config or the credentials restarts the # daemon here, before verification reads its state back. - name: Daemon | Flush handlers ansible.builtin.meta: flush_handlers