§
    øžyj�¼  ã                  óØ  — d Z ddlmZ ddlZddlmZ ddlmZmZm	Z	 ed         Z
 ej        d¦  «        ZdZd/d„Ze	 G d„ de¦  «        ¦   «         Zd0d„Zd1d„Z G d„ d¦  «        Z G d„ de¦  «        Z G d„ de¦  «        Z G d„ de¦  «        Zd2d„Z ed¦  «        ZdZd3d „Zd4d"„Zd#Zd$Zd$Zd5d&„Z G d'„ d(e¦  «        Z  G d)„ d*e ¦  «        Z! G d+„ d,e ¦  «        Z" G d-„ d.¦  «        Z#dS )6u=  Abstract service manager interface.

Wraps the existing systemd (Linux host), launchd (macOS host), Windows
Scheduled Task (native Windows host), and s6 (container) backends behind
a common Protocol. Only the s6 backend supports runtime registration
(for per-profile gateways) â€” host backends raise NotImplementedError
from those methods, and callers MUST check supports_runtime_registration()
before invoking them.

Host-side call sites (setup wizard, uninstall, status) continue to use
the existing module-level functions in hermes_cli.gateway and
hermes_cli.gateway_windows directly. This protocol is a thin facade
used by new code that needs to be backend-agnostic â€” specifically the
profile create/delete hooks (Phase 4) and the s6 dispatch path in
``hermes gateway start/stop/restart`` when running inside a container.
é    )ÚannotationsN)ÚPath)ÚLiteralÚProtocolÚruntime_checkable)ÚsystemdÚlaunchdÚwindowsÚs6Únonez^[a-z0-9][a-z0-9_-]*$éû   ÚnameÚstrÚreturnÚNonec                ó   — | st          d¦  «        ‚t          | ¦  «        t          k    r(t          dt          | ¦  «        › dt          › d�¦  «        ‚t                               | ¦  «        st          d| ›�¦  «        ‚dS )aA  Raise ValueError if ``name`` is not usable as a profile name.

    Profile names are used as s6 service directory names, so they must
    match a conservative subset of filesystem-safe characters. Reject
    empty strings, uppercase, paths-traversal sequences, and anything
    longer than s6's default ``name_max``.
    zprofile name must not be emptyzprofile name too long (z > ú)z1profile name must match [a-z0-9][a-z0-9_-]*, got N)Ú
ValueErrorÚlenÚ_MAX_PROFILE_LENÚ_VALID_PROFILE_REÚmatch)r   s    ú@/home/ragecks/.hermes/hermes-agent/hermes_cli/service_manager.pyÚvalidate_profile_namer   !   s›   € ð ð ;ÝÐ9Ñ:Ô:Ð:Ý
ˆ4�y„yÕ#Ò#Ð#ÝØG¥c¨$¡i¤iÐGÐGÕ4DÐGÐGÐGñ
ô 
ð 	
õ ×"Ò" 4Ñ(Ô(ð 
ÝØHÀÐHÐHñ
ô 
ð 	
ð
ð 
ó    c                  óf   — e Zd ZU dZded<   dd„Zdd	„Zdd
„Zdd„Zdd„Z	dddœdd„Z
dd„Zdd„ZdS )ÚServiceManageru§  Abstract interface for init-system-specific service operations.

    Lifecycle methods (start / stop / restart / is_running) are
    implemented by every backend. Runtime registration
    (register_profile_gateway / unregister_profile_gateway /
    list_profile_gateways) is implemented only by the s6 backend â€”
    callers MUST check ``supports_runtime_registration()`` before
    invoking the registration methods.
    ÚServiceManagerKindÚkindr   r   r   r   c                ó   — d S ©N© ©Úselfr   s     r   ÚstartzServiceManager.startD   ó   € € € r   c                ó   — d S r!   r"   r#   s     r   ÚstopzServiceManager.stopE   r&   r   c                ó   — d S r!   r"   r#   s     r   ÚrestartzServiceManager.restartF   r&   r   Úboolc                ó   — d S r!   r"   r#   s     r   Ú
is_runningzServiceManager.is_runningG   r&   r   c                ó   — d S r!   r"   ©r$   s    r   Úsupports_runtime_registrationz,ServiceManager.supports_runtime_registrationJ   r&   r   NT©Ú	extra_envÚ	start_nowÚprofiler2   údict[str, str] | Noner3   c               ó   — d S r!   r"   ©r$   r4   r2   r3   s       r   Úregister_profile_gatewayz'ServiceManager.register_profile_gatewayK   s	   € ð ˆsr   c                ó   — d S r!   r"   ©r$   r4   s     r   Úunregister_profile_gatewayz)ServiceManager.unregister_profile_gatewayR   r&   r   ú	list[str]c                ó   — d S r!   r"   r/   s    r   Úlist_profile_gatewaysz$ServiceManager.list_profile_gatewaysS   r&   r   ©r   r   r   r   ©r   r   r   r+   ©r   r+   ©r4   r   r2   r5   r3   r+   r   r   ©r4   r   r   r   ©r   r<   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__Ú__annotations__r%   r(   r*   r-   r0   r8   r;   r>   r"   r   r   r   r   5   s«   € € € € € € ðð ð ÐÐÑð ,Ð+Ð+Ð+Ø*Ð*Ð*Ð*Ø-Ð-Ð-Ð-Ø0Ð0Ð0Ð0ð 9Ð8Ð8Ð8ð
 ,0Øðð ð ð ð ð ð DÐCÐCÐCØ5Ð5Ð5Ð5Ð5Ð5r   r   r   c                 ó‚   — ddl m} m}m} t	          ¦   «         rdS  |¦   «         rdS  | ¦   «         rdS  |¦   «         rdS dS )u‘  Detect which service manager is available in this environment.

    Returns:
        "s6" â€” s6-svscan is PID 1 (s6-overlay image; Docker, Podman, or a
               Fly Firecracker microVM)
        "windows" â€” native Windows host
        "launchd" â€” macOS host
        "systemd" â€” Linux host with a working user/system bus
        "none" â€” anything else (Termux, sandbox shells, etc.)

    This function does NOT replace ``supports_systemd_services()`` â€”
    host call sites continue to use that. It exists for new backend-
    agnostic code (profile create/delete hooks, the s6 dispatch path
    in ``hermes gateway start/stop/restart``).
    r   ©Úis_macosÚ
is_windowsÚsupports_systemd_servicesr   r
   r	   r   r   )Úhermes_cli.gatewayrL   rM   rN   Ú_s6_runningrK   s      r   Údetect_service_managerrQ   V   sˆ   € ð&ð ð ð ð ð ð ð ð ð õ �}„}ð ØˆtØ€z�|„|ð ØˆyØ€x�z„zð ØˆyØ Ð Ñ"Ô"ð ØˆyØˆ6r   r+   c                 óä   — 	 t          d¦  «                             d¬¦  «                             ¦   «         } n# t          $ r Y dS w xY w| dk    rdS t          d¦  «                             ¦   «         S )u¥  True when s6-svscan is running as PID 1 in this container.

    Detection has to work for **both** root and the unprivileged hermes
    user (UID 10000). The obvious probe â€” ``Path('/proc/1/exe').resolve()``
    â€” only works as root: for any other UID, the symlink at
    ``/proc/1/exe`` is unreadable and ``resolve()`` silently returns the
    path unchanged, so the resolved name is the literal ``"exe"`` and
    detection always fails. Since every Hermes runtime call inside the
    container drops to hermes via ``s6-setuidgid``, that silent failure
    made the entire service-manager runtime-registration path inert in
    production (PR #30136 review).

    Probe instead via:
      * ``/proc/1/comm`` â€” world-readable, contains the process comm
        (``s6-svscan`` when s6-overlay is PID 1).
      * ``/run/s6/basedir`` â€” s6-overlay-specific directory created by
        stage1. World-readable. More specific than ``/run/s6`` (which
        other tools occasionally create).

    Both signals are required; either alone could false-positive
    (e.g. a container with the s6 binaries installed but a different
    init, or an unrelated process named ``s6-svscan``).
    z/proc/1/commúutf-8©ÚencodingFz	s6-svscanz/run/s6/basedir)r   Ú	read_textÚstripÚOSErrorÚis_dir)Úcomms    r   rP   rP   �   s€   € ð0Ý�NÑ#Ô#×-Ò-°wÐ-Ñ?Ô?×EÒEÑGÔGˆˆøÝð ð ð Øˆuˆuðøøøàˆ{ÒÐØˆuÝÐ!Ñ"Ô"×)Ò)Ñ+Ô+Ð+s   ‚58 ¸
AÁAc                  ó:   — e Zd ZdZdd„Zdddœdd„Zdd„Zdd„ZdS )Ú_RegistrationUnsupportedMixinz@Mixin for host backends that don't support runtime registration.r   r+   c                ó   — dS )NFr"   r/   s    r   r0   z;_RegistrationUnsupportedMixin.supports_runtime_registration²   s   € Øˆur   NTr1   r4   r   r2   r5   r3   r   c               óJ   — t          t          | ¦  «        j        › d�¦  «        ‚)NzO does not support runtime profile gateway registration (container-only feature)©ÚNotImplementedErrorÚtyperE   r7   s       r   r8   z6_RegistrationUnsupportedMixin.register_profile_gatewayµ   s1   € õ "Ý�D‰zŒzÔ"ð <ð <ð <ñ
ô 
ð 	
r   c                óJ   — t          t          | ¦  «        j        › d�¦  «        ‚)NzQ does not support runtime profile gateway unregistration (container-only feature)r_   r:   s     r   r;   z8_RegistrationUnsupportedMixin.unregister_profile_gatewayÁ   s/   € Ý!Ý�D‰zŒzÔ"ð >ð >ð >ñ
ô 
ð 	
r   r<   c                ó   — g S r!   r"   r/   s    r   r>   z3_RegistrationUnsupportedMixin.list_profile_gatewaysÇ   s   € Øˆ	r   rA   rB   rC   rD   )rE   rF   rG   rH   r0   r8   r;   r>   r"   r   r   r\   r\   ¯   s|   € € € € € ØJÐJðð ð ð ð ,0Øð

ð 

ð 

ð 

ð 

ð 

ð
ð 
ð 
ð 
ðð ð ð ð ð r   r\   c                  óB   — e Zd ZU dZdZded<   dd	„Zdd
„Zdd„Zdd„Z	dS )ÚSystemdServiceManagera	  Thin wrapper around the ``systemd_*`` functions in hermes_cli.gateway.

    Existing host call sites continue to use those functions directly;
    this wrapper exists for new code that needs to be backend-agnostic
    (the Phase 4 profile create/delete hooks).
    r   r   r   r   r   r   r   c                ó&   — ddl m}  |¦   «          d S )Nr   )Úsystemd_start)rO   rg   )r$   r   rg   s      r   r%   zSystemdServiceManager.startÕ   ó#   € Ø4Ð4Ð4Ð4Ð4Ð4Øˆ‰Œˆˆˆr   c                ó&   — ddl m}  |¦   «          d S )Nr   )Úsystemd_stop)rO   rj   )r$   r   rj   s      r   r(   zSystemdServiceManager.stopÙ   ó#   € Ø3Ð3Ð3Ð3Ð3Ð3Øˆ‰Œˆˆˆr   c                ó&   — ddl m}  |¦   «          d S )Nr   )Úsystemd_restart)rO   rm   )r$   r   rm   s      r   r*   zSystemdServiceManager.restartÝ   ó(   € Ø6Ð6Ð6Ð6Ð6Ð6ØˆÑÔÐÐÐr   r+   c                ó,   — ddl m}  |¦   «         \  }}|S )Nr   )Ú_probe_systemd_service_running)rO   rp   )r$   r   rp   Ú_Úrunnings        r   r-   z SystemdServiceManager.is_runningá   s+   € ØEÐEÐEÐEÐEÐEØ3Ð3Ñ5Ô5‰
ˆˆ7Øˆr   Nr?   r@   ©
rE   rF   rG   rH   r   rI   r%   r(   r*   r-   r"   r   r   re   re   Ë   sƒ   € € € € € € ðð ð  )€DÐ(Ð(Ð(Ñ(ðð ð ð ðð ð ð ðð ð ð ðð ð ð ð ð r   re   c                  óB   — e Zd ZU dZdZded<   dd	„Zdd
„Zdd„Zdd„Z	dS )ÚLaunchdServiceManagerzFThin wrapper around the ``launchd_*`` functions in hermes_cli.gateway.r	   r   r   r   r   r   r   c                ó&   — ddl m}  |¦   «          d S )Nr   )Úlaunchd_start)rO   rw   )r$   r   rw   s      r   r%   zLaunchdServiceManager.startì   rh   r   c                ó&   — ddl m}  |¦   «          d S )Nr   )Úlaunchd_stop)rO   ry   )r$   r   ry   s      r   r(   zLaunchdServiceManager.stopð   rk   r   c                ó&   — ddl m}  |¦   «          d S )Nr   )Úlaunchd_restart)rO   r{   )r$   r   r{   s      r   r*   zLaunchdServiceManager.restartô   rn   r   r+   c                ó"   — ddl m}  |¦   «         S )Nr   )Ú_probe_launchd_service_running)rO   r}   )r$   r   r}   s      r   r-   z LaunchdServiceManager.is_runningø   s#   € ØEÐEÐEÐEÐEÐEØ-Ð-Ñ/Ô/Ð/r   Nr?   r@   rs   r"   r   r   ru   ru   ç   s}   € € € € € € ØPÐPà(€DÐ(Ð(Ð(Ñ(ðð ð ð ðð ð ð ðð ð ð ð0ð 0ð 0ð 0ð 0ð 0r   ru   c                  óV   — e Zd ZU dZdZded<   dddddœdd„Zdd„Zdd„Zdd„Z	dd„Z
dS )ÚWindowsServiceManageru  Thin wrapper around ``hermes_cli.gateway_windows`` (Scheduled Task /
    Startup-folder fallback).

    The native Windows backend uses a Scheduled Task rather than a true
    init-system service, but for protocol purposes the lifecycle is the
    same: start / stop / restart / is_running. ``install`` accepts a
    handful of Windows-specific kwargs (start_now, start_on_login,
    elevated_handoff) that are passed straight through â€” non-Windows
    callers should never invoke ``install`` on this wrapper.
    r
   r   r   FN©Úforcer3   Ústart_on_loginÚelevated_handoffr�   r+   r3   úbool | Noner‚   rƒ   r   r   c               óD   — ddl m} |                     ||||¬¦  «         d S )Nr   ©Úgateway_windowsr€   )Ú
hermes_clir‡   Úinstall)r$   r�   r3   r‚   rƒ   r‡   s         r   r‰   zWindowsServiceManager.install  sI   € ð 	/Ð.Ð.Ð.Ð.Ð.Ø×ÒØØØ)Ø-ð	 	 ñ 	
ô 	
ð 	
ð 	
ð 	
r   r   r   c                ó:   — ddl m} |                     ¦   «          d S ©Nr   r†   )rˆ   r‡   r%   ©r$   r   r‡   s      r   r%   zWindowsServiceManager.start  s,   € Ø.Ð.Ð.Ð.Ð.Ð.Ø×ÒÑÔÐÐÐr   c                ó:   — ddl m} |                     ¦   «          d S r‹   )rˆ   r‡   r(   rŒ   s      r   r(   zWindowsServiceManager.stop  s,   € Ø.Ð.Ð.Ð.Ð.Ð.Ø×ÒÑÔÐÐÐr   c                ó:   — ddl m} |                     ¦   «          d S r‹   )rˆ   r‡   r*   rŒ   s      r   r*   zWindowsServiceManager.restart#  s,   € Ø.Ð.Ð.Ð.Ð.Ð.Ø×ÒÑ!Ô!Ð!Ð!Ð!r   c                ót   — ddl m} ddlm} |                     ¦   «         sdS t           |¦   «         ¦  «        S )Nr   r†   )Úfind_gateway_pidsF)rˆ   r‡   rO   r�   Úis_installedr+   )r$   r   r‡   r�   s       r   r-   z WindowsServiceManager.is_running'  sW   € Ø.Ð.Ð.Ð.Ð.Ð.Ø8Ð8Ð8Ð8Ð8Ð8Ø×+Ò+Ñ-Ô-ð 	Ø�5ÝÐ%Ð%Ñ'Ô'Ñ(Ô(Ð(r   )
r�   r+   r3   r„   r‚   r„   rƒ   r+   r   r   r?   r@   )rE   rF   rG   rH   r   rI   r‰   r%   r(   r*   r-   r"   r   r   r   r   ý   s¯   € € € € € € ð	ð 	ð  )€DÐ(Ð(Ð(Ñ(ð
 Ø!%Ø&*Ø!&ð
ð 
ð 
ð 
ð 
ð 
ð  ð  ð  ð  ðð ð ð ð"ð "ð "ð "ð)ð )ð )ð )ð )ð )r   r   c                 óÜ   — t          ¦   «         } | dk    rt          ¦   «         S | dk    rt          ¦   «         S | dk    rt          ¦   «         S | dk    rt	          ¦   «         S t          d¦  «        ‚)z�Return the ServiceManager instance for the current environment.

    Raises:
        RuntimeError: when no supported backend is available.
    r   r	   r
   r   z%no supported service manager detected)rQ   re   ru   r   ÚS6ServiceManagerÚRuntimeError)r   s    r   Úget_service_managerr•   /  sx   € õ "Ñ#Ô#€DØˆyÒÐÝ$Ñ&Ô&Ð&ØˆyÒÐÝ$Ñ&Ô&Ð&ØˆyÒÐÝ$Ñ&Ô&Ð&Øˆt‚|€|ÝÑ!Ô!Ð!Ý
Ð>Ñ
?Ô
?Ð?r   z/run/serviceúgateway-r   c                óF  — ddl }|                      t          ¦  «        r| t          t          ¦  «        d…         n| }t	          |¦  «         t          |j                             dd¦  «        ¦  «        }|j        j	        dk    r|j        j        }n|}|dk    r|n|dz  |z  S )a‡  Resolve ``gateway-<profile>`` to its persistent profile directory.

    s6 lifecycle commands may be invoked from any active profile, including
    ``gateway stop --all``. Do not write the caller's HERMES_HOME blindly;
    derive the shared profile root from the current HERMES_HOME and map the
    service suffix to either the root default profile or
    ``<root>/profiles/<profile>``.
    r   NÚHERMES_HOMEz	/opt/dataÚprofilesÚdefault)
ÚosÚ
startswithÚS6_SERVICE_PREFIXr   r   r   ÚenvironÚgetÚparentr   )r   r›   r4   Úhermes_homeÚroots        r   Ú _profile_dir_for_gateway_servicer£   R  sª   € ð €I€I€Ià/3¯ªÕ?PÑ/QÔ/QÐ[ˆd•3Õ(Ñ)Ô)Ð*Ð*Ô+Ð+ÐW[€GÝ˜'Ñ"Ô"Ð"Ý�r”z—~’~ m°[ÑAÔAÑBÔB€KØÔÔ *Ò,Ð,ØÔ!Ô(ˆˆàˆØ˜iÒ'Ð'ˆ4ˆ4¨T°JÑ->ÀÑ-HÐHr   Údesired_statec                ó„  — ddl }ddl}t          | ¦  «        }|dz  }	 |                     ¦   «         sdS 	 |                     ¦   «         r)|                     |                     d¬¦  «        ¦  «        ni }t          |t          ¦  «        si }n# t          |j	        f$ r i }Y nw xY w||d<   t          |                     ¦   «         ¦  «        |d<   |                     |j        dz   ¦  «        }|                     |                     |d	¬
¦  «        dz   d¬¦  «         |                     |¦  «         dS # t          $ r Y dS w xY w)a  Persist durable s6 gateway intent next to runtime status.

    ``gateway_state`` remains the volatile runtime field written by the
    gateway process. ``desired_state`` records the operator's start/stop
    intent so container-boot reconciliation can restore the correct s6
    want-up/want-down state after pod recreation even if the previous runtime
    state was transient (draining, startup_failed, etc.). The write is
    best-effort: a failed persistence attempt must not prevent immediate s6
    lifecycle control.
    r   Nzgateway_state.jsonrS   rT   r¤   Ú
updated_atú.tmp)Ú,ú:)Ú
separatorsÚ
)ÚjsonÚtimer£   ÚexistsÚloadsrV   Ú
isinstanceÚdictrX   ÚJSONDecodeErrorÚintÚwith_suffixÚsuffixÚ
write_textÚdumpsÚreplace)r   r¤   r¬   r­   Úprofile_dirÚ
state_fileÚdataÚtmps           r   Ú_write_gateway_desired_stater½   g  ss  € ð €K€K€KØ€K€K€Kå2°4Ñ8Ô8€KØÐ3Ñ3€JðØ×!Ò!Ñ#Ô#ð 	ØˆFð	ØIS×IZÒIZÑI\ÔI\Ðd�4—:’:˜j×2Ò2¸GÐ2ÑDÔDÑEÔEÐEÐbdˆDÝ˜d¥DÑ)Ô)ð Ø�øøÝ˜Ô-Ð.ð 	ð 	ð 	ØˆDˆDˆDð	øøøà -ˆˆ_ÑÝ  §¢¡¤Ñ-Ô-ˆˆ\ÑØ×$Ò$ ZÔ%6¸Ñ%?Ñ@Ô@ˆØ�Š�t—z’z $°:�zÑ>Ô>ÀÑEÐPWˆÑXÔXÐXØ�Š�JÑÔÐÐÐøÝð ð ð Øˆˆðøøøs6   žD1 µAB ÂD1 ÂB"ÂD1 Â!B"Â"BD1 Ä1
D?Ä>D?z/commandi'  Úsvc_dirc                óî  ‡— ddl Šdˆfd	„} || d
z  d¦  «         | dz  } ||d¦  «          ||d
z  d¦  «         |dz  }|                     ¦   «         s^‰                     |d¦  «         |                     d¦  «         	 ‰                     |t
          t          ¦  «         n# t          $ r Y nw xY w| dz  }|                     ¦   «         r¨ ||d
z  d¦  «         |dz  } ||d¦  «          ||d
z  d¦  «         |dz  }|                     ¦   «         sb‰                     |d¦  «         |                     d¦  «         	 ‰                     |t
          t          ¦  «         dS # t          $ r Y dS w xY wdS dS )u  Pre-create the ``supervise/`` and top-level ``event/`` skeleton
    inside a service directory, owned by the hermes user.

    Why this exists
    ---------------
    When s6-supervise spawns a service it tries to ``mkdir`` two
    directories: ``<svc>/event`` and ``<svc>/supervise``, both with mode
    ``0700``. It also ``mkfifo``s ``<svc>/supervise/control`` with mode
    ``0600``. Because s6-supervise runs as PID 1's effective UID (root)
    these dirs end up root-owned mode 0700, and an unprivileged client
    (the ``hermes`` user â€” UID 10000 â€” running every Hermes runtime
    operation via ``s6-setuidgid``) gets ``EACCES`` on any ``s6-svc``,
    ``s6-svstat``, or ``s6-svwait`` invocation against the slot.

    The PR #30136 review surfaced this as a real product gap: the
    entire S6ServiceManager lifecycle (``register/start/stop/unregister
    _profile_gateway``) was inert in production because every operation
    is dispatched as the hermes user.

    Why this works
    --------------
    Reading s6's source (src/supervision/s6-supervise.c::trymkdir +
    control_init): the ``mkdir`` and ``mkfifo`` calls both treat
    ``EEXIST`` as success. If the directory is already present, the
    chown/chmod fix-up that would normally make event/ ``03730
    root:root`` is **skipped** entirely â€” s6-supervise just opens the
    pre-existing FIFOs and proceeds. So if we lay the skeleton down
    with hermes ownership before triggering ``s6-svscanctl -a``,
    s6-supervise inherits our layout and never touches it.

    Layout produced
    ---------------
    ``svc_dir/``                           hermes:hermes, 0755 (parent must already exist)
    ``svc_dir/event/``                     hermes:hermes, 03730   (setgid + g+rwx + sticky)
    ``svc_dir/supervise/``                 hermes:hermes, 0755
    ``svc_dir/supervise/event/``           hermes:hermes, 03730
    ``svc_dir/supervise/control``          hermes:hermes, 0660    (FIFO)

    The ``death_tally``, ``lock``, and ``status`` regular files end up
    written by s6-supervise itself (as root), but those land mode 0644 â€”
    world-readable â€” and ``s6-svstat`` only needs read access, so the
    hermes user reads them fine.

    If ``svc_dir/log/`` is present (the canonical s6 logger pattern â€”
    one s6-supervise instance per service, plus a second for its
    logger), the same skeleton is seeded under ``log/`` as well:
    ``log/event/``, ``log/supervise/``, ``log/supervise/event/``,
    ``log/supervise/control``. Without this, unregister teardown
    would EACCES on the logger's supervise dir even after the parent
    slot's supervise/ was hermes-owned.

    Idempotency
    -----------
    Safe to call against a directory where the skeleton already exists.
    Existing entries are left untouched (the helper doesn't try to
    re-chown / re-chmod live FIFOs that s6-supervise may have already
    opened).

    Reference
    ---------
    Discussed at length on the skarnet `skaware` mailing list in 2020
    (`<http://skarnet.org/lists/skaware/1424.html>`_); see also
    just-containers/s6-overlay#130. The pre-creation pattern was
    historically called out as forward-compatibility-fragile, but the
    EEXIST handling in s6-supervise has been stable since 2015 â€” it's
    the same pattern ``s6-svperms`` and ``fix-attrs.d`` rely on.
    r   NÚpathr   Úmoder³   r   r   c                óò   •— |                       ¦   «         rd S |                      dd¬¦  «         |                      |¦  «         	 ‰                     | t          t
          ¦  «         d S # t          $ r Y d S w xY w)NF)ÚparentsÚexist_ok)r®   ÚmkdirÚchmodÚchownÚ_HERMES_UIDÚ_HERMES_GIDÚPermissionError)rÀ   rÁ   r›   s     €r   Ú_mkdir_ownedz._seed_supervise_skeleton.<locals>._mkdir_ownedæ  s…   ø€ Ø�;Š;‰=Œ=ð 	ØˆFØ�
Š
˜5¨5ˆ
Ñ1Ô1Ð1Ø�
Š
�4ÑÔÐð	Ø�HŠH�T�;­Ñ4Ô4Ð4Ð4Ð4øÝð 	ð 	ð 	ð
 ˆDˆDð	øøøs   Á!A( Á(
A6Á5A6ÚeventiØ  Ú	superviseéí  Úcontroli°  Úlog)rÀ   r   rÁ   r³   r   r   )	r›   r®   ÚmkfiforÆ   rÇ   rÈ   rÉ   rÊ   rY   )r¾   rË   rÍ   rÏ   Úlog_dirÚlog_superviseÚlog_controlr›   s          @r   Ú_seed_supervise_skeletonrÕ      s  ø€ ðH €I€I€Iðð ð ð ð ð ð  €L�˜7Ñ" FÑ+Ô+Ð+ð ˜+Ñ%€IØ€L�˜EÑ"Ô"Ð"Ø€L�˜WÑ$ fÑ-Ô-Ð-ð ˜)Ñ#€GØ�>Š>ÑÔð Ø
�	Š	�'˜5Ñ!Ô!Ð!Ø�Š�eÑÔÐð	Ø�HŠH�W�k­;Ñ7Ô7Ð7Ð7øÝð 	ð 	ð 	ØˆDð	øøøð ˜‰o€GØ‡~‚~ÑÔð Øˆ�W˜wÑ&¨Ñ/Ô/Ð/Ø +Ñ-ˆØˆ�] EÑ*Ô*Ð*Øˆ�] WÑ,¨fÑ5Ô5Ð5Ø# iÑ/ˆØ×!Ò!Ñ#Ô#ð 	Ø�IŠI�k 5Ñ)Ô)Ð)Ø×Ò˜eÑ$Ô$Ð$ðØ—’˜¥kµ;Ñ?Ô?Ð?Ð?Ð?øÝ"ð ð ð Ø��ðøøøðð ð	ð 	s$   Â !B" Â"
B/Â.B/Ä?!E" Å"
E0Å/E0c                  ó*   ‡ — e Zd ZdZddœdˆ fd
„Zˆ xZS )ÚS6Errora
  Base error for S6ServiceManager lifecycle failures.

    Concrete subclasses carry the slot name (and, where useful, the
    underlying subprocess output) so the CLI can render an actionable
    message instead of leaking a raw ``CalledProcessError`` traceback.
    N©ÚserviceÚmessager   rÙ   ú
str | Noner   r   c               óX   •— t          ¦   «                              |¦  «         || _        d S r!   )ÚsuperÚ__init__rÙ   )r$   rÚ   rÙ   Ú	__class__s      €r   rÞ   zS6Error.__init__+  s&   ø€ Ý‰Œ×Ò˜Ñ!Ô!Ð!ØˆŒˆˆr   )rÚ   r   rÙ   rÛ   r   r   ©rE   rF   rG   rH   rÞ   Ú__classcell__©rß   s   @r   r×   r×   #  sW   ø€ € € € € ðð ð ?Cð ð ð ð ð ð ð ð ð ð ð ð r   r×   c                  ó$   ‡ — e Zd ZdZdˆ fd„Zˆ xZS )ÚGatewayNotRegisteredErroraM  Raised when a lifecycle method targets a slot that doesn't exist.

    Most commonly: ``hermes -p typo gateway start`` when no profile
    ``typo`` exists. Carries the unprefixed profile name (not the
    full ``gateway-<profile>`` service-dir name) so callers can phrase
    a user-facing message like "no such gateway 'typo'".
    r4   r   r   r   c                óp   •— || _         t          ¦   «                              d|›d|› d�d|› �¬¦  «         d S )Nzno such gateway z*: register it with `hermes profile create z9` first, or pass an existing profile name via `-p <name>`r–   rØ   )r4   rÝ   rÞ   )r$   r4   rß   s     €r   rÞ   z"GatewayNotRegisteredError.__init__9  se   ø€ ØˆŒÝ‰Œ×Òð7˜wð 7ð 7Ø&-ð7ð 7ð 7ð )˜wÐ(Ð(ð	 	ñ 	
ô 	
ð 	
ð 	
ð 	
r   rC   rà   râ   s   @r   rä   rä   0  sG   ø€ € € € € ðð ð
ð 
ð 
ð 
ð 
ð 
ð 
ð 
ð 
ð 
r   rä   c                  ó$   ‡ — e Zd ZdZdˆ fd
„Zˆ xZS )ÚS6CommandErroru  Raised when an s6 command fails for a reason other than a
    missing slot â€” e.g. permission denied on the supervise control
    FIFO, or s6-svc returning a non-zero exit for an unexpected
    reason. Carries the stderr from the failing command so callers
    can surface it.
    rÙ   r   ÚactionÚ
returncoder³   Ústderrr   r   c               óì   •— || _         || _        || _        d|› d|›d|› d�}|                     ¦   «         r|d|                     ¦   «         › �z  }t	          ¦   «                              ||¬¦  «         d S )Nzs6-svc z on z failed (rc=r   z: rØ   )rè   ré   rê   rW   rÝ   rÞ   )r$   rÙ   rè   ré   rê   rÚ   rß   s         €r   rÞ   zS6CommandError.__init__K  s�   ø€ ð ˆŒØ$ˆŒØˆŒàF�fÐFÐF 'ÐFÐF¸ÐFÐFÐFð 	ð �<Š<‰>Œ>ð 	-ØÐ,˜FŸLšL™NœNÐ,Ð,Ñ,ˆGÝ‰Œ×Ò˜¨'ÐÑ2Ô2Ð2Ð2Ð2r   )
rÙ   r   rè   r   ré   r³   rê   r   r   r   rà   râ   s   @r   rç   rç   C  sG   ø€ € € € € ðð ð3ð 3ð 3ð 3ð 3ð 3ð 3ð 3ð 3ð 3r   rç   c                  óÞ   — e Zd ZU dZdZded<   efd(d	„Zd)d„Zd*d„Z	e
d+d„¦   «         Ze
d,d„¦   «         Ze
d*d„¦   «         Zd-d„Zd.d„Zd/d„Zd.d„Zd.d„Zd0d„Zd1d„Zdd d!œd2d$„Zd3d%„Zd4d'„ZdS )5r“   zðPer-profile gateway supervision via s6-overlay.

    Only handles runtime-registered services under
    ``S6_DYNAMIC_SCANDIR``. Static services (main-hermes, dashboard)
    are managed by s6-rc at image-build time and are out of scope.
    r   r   r   Úscandirr   r   r   c                ó   — || _         d S r!   )rí   )r$   rí   s     r   rÞ   zS6ServiceManager.__init__c  s   € ØˆŒˆˆr   r4   r   c                óF   — t          |¦  «         | j        t          › |› �z  S r!   )r   rí   r�   r:   s     r   Ú_service_dirzS6ServiceManager._service_dirh  s)   € Ý˜gÑ&Ô&Ð&ØŒ|Õ!2Ð=°GÐ=Ð=Ñ=Ð=r   c                ó   — t           › |› �S r!   )r�   r:   s     r   Ú_service_namezS6ServiceManager._service_namel  s   € Ý#Ð. WÐ.Ð.Ð.r   r2   údict[str, str]c           	     óº  — ddl }g d¢}t          |                     ¦   «         ¦  «        D ]3\  }}|                     d|› d|                     |¦  «        › �¦  «         Œ4|                     d¦  «         | dk    rd}nd	|                     | ¦  «        › d
�}|                     d|› �¦  «         |                     d|› �¦  «         d                     |¦  «        dz   S )u>	  Generate the run script for a profile-gateway s6 service.

        The script:
          1. Sources HERMES_HOME (and any extra env) via with-contenv â€”
             so e.g. ``-e HERMES_HOME=/data/hermes`` is honored at run
             time, not Python-substituted at registration time (OQ8-C).
          2. Resets ``HOME`` to ``/opt/data`` before the privilege drop
             so with-contenv's root HOME does not leak into the
             unprivileged gateway process.
          3. Activates the bundled venv.
          4. Drops to the hermes user and exec's
             ``hermes -p <profile> gateway run`` (or just ``hermes
             gateway run`` for the default profile â€” see below).

        Special case: ``profile == "default"`` emits ``hermes gateway
        run`` with **no** ``-p`` flag. This is the sentinel for "the
        root HERMES_HOME profile" (the implicit profile that exists at
        the top of $HERMES_HOME, not under profiles/). It must be
        spelled this way because ``_profile_suffix()`` returns the
        empty string for the root profile, and the dispatcher in
        ``hermes_cli.gateway`` maps that empty string to the
        ``gateway-default`` service slot. Passing ``-p default`` here
        would instead look up ``$HERMES_HOME/profiles/default/`` â€” a
        completely different (and almost always nonexistent) profile.

        Port selection: the gateway binds the port resolved by
        ``gateway/config.py`` from the profile's own environment â€”
        ``API_SERVER_PORT`` (or ``platforms.api_server.extra.port`` in
        that profile's ``config.yaml``), defaulting to 8642. There is
        no ``[gateway] port`` key and no Python-side allocator: because
        each supervised profile gateway loads its own ``HERMES_HOME``,
        two profiles that both leave the port unset will both try to
        bind 8642 â€” give each profile a distinct ``API_SERVER_PORT`` in
        its ``.env``. Previously this method took a ``port`` parameter
        that was passed in but never substituted into the rendered
        script (carried for "API parity" with a deterministic SHA-256
        allocator in ``hermes_cli.profiles._allocate_gateway_port``).
        PR #30136 review item I5 retired both the allocator and the
        parameter because they were dead code through the entire stack.
        r   N)z#!/command/with-contenv shz# shellcheck shell=shzset -ezexport HOME=/opt/datazcd /opt/dataz . /opt/hermes/.venv/bin/activatezexport Ú=z#export HERMES_S6_SUPERVISED_CHILD=1rš   zhermes gateway run --replacez
hermes -p z gateway run --replacez[ "$(id -u)" = 0 ] || exec zexec s6-setuidgid hermes r«   )ÚshlexÚsortedÚitemsÚappendÚquoteÚjoin)r4   r2   rö   ÚlinesÚkÚvÚgateway_cmds          r   Ú_render_run_scriptz#S6ServiceManager._render_run_scripto  s
  € ðZ 	ˆˆˆð
ð 
ð 
ˆõ ˜9Ÿ?š?Ñ,Ô,Ñ-Ô-ð 	9ð 	9‰DˆAˆqØ�LŠLÐ7 1Ð7Ð7 u§{¢{°1¡~¤~Ð7Ð7Ñ8Ô8Ð8Ð8ð 	�ŠÐ:Ñ;Ô;Ð;ð  �iÒÐØ8ˆKˆKàS u§{¢{°7Ñ';Ô';ÐSÐSÐSˆKð 	�ŠÐ@°;Ð@Ð@ÑAÔAÐAØ�ŠÐ>°Ð>Ð>Ñ?Ô?Ð?Ø�yŠy˜ÑÔ $Ñ&Ð&r   c                 ó$   — ddl m}  | }d|› d|› d�S )uy  Generate the finish script for a profile-gateway s6 service.

        When the gateway exits with EX_CONFIG (78) â€” a fatal
        configuration error such as a token collision or no messaging
        platforms â€” we tell s6-supervise to stop restarting by exiting
        125 (permanent failure).  Any other exit code lets s6 restart
        normally.  See #51228.
        r   )ÚGATEWAY_FATAL_CONFIG_EXIT_CODEz^#!/command/with-contenv sh
# shellcheck shell=sh
# $1 = exit code from the run script.
# Exit uB    (EX_CONFIG) = fatal config error â€” don't restart.
if [ "$1" = "z" ]; then
  exit 125
fi
exit 0
)Úgateway.restartr  )r  Úcodes     r   Ú_render_finish_scriptz&S6ServiceManager._render_finish_scriptÈ  sI   € ð 	CÐBÐBÐBÐBÐBà-ˆðð ðð ð !ð	ð ð ð		
r   c                ó@   — ddl }|                     | ¦  «        }d|› d�S )u¦  Generate the log/run script for a profile-gateway service.

        OQ8-C: persist to ``${HERMES_HOME}/logs/gateways/<profile>/``.
        CRITICAL: the HERMES_HOME path is sourced from the runtime env
        via with-contenv â€” NOT Python-substituted at registration time
        â€” so a container started with ``-e HERMES_HOME=/data/hermes``
        gets its logs under /data/hermes/logs/..., not the build-time
        default.

        Output routing â€” the script is two action directives, applied
        per line, in order:

          1. ``1`` (forward to stdout) â€” propagates the line up the
             s6-supervise pipeline to /init's stdout, which is the
             container's stdout, which is ``docker logs``. Without
             this, supervised stdout would be terminated inside
             s6-log and never reach the container's log stream;
             users would have to ``docker exec`` and ``tail`` the
             file just to see startup banners. (Python's ``logging``
             module defaults to stderr, which s6-supervise leaves
             unfiltered â€” so warnings/errors already reach docker
             logs. This change is specifically about the rich-console
             banner output and other plain stdout writes.)
          2. ``T <log_dir>`` â€” also write a timestamped copy to the
             rotated log directory (``current`` + archived ``@*.s``
             files). This is what ``hermes logs`` reads and what
             persists across container restarts via the volume mount.

        ``T`` is non-sticky: it only prefixes lines for the next
        action directive. We deliberately put ``T`` between ``1``
        and the log dir (not before ``1``) so:

          * ``docker logs`` shows raw lines â€” Python's logging
            formatter has its own timestamps, and ``docker logs
            --timestamps`` adds a third layer when desired. No
            double-stamping in the most common reading path.
          * The persisted file gets s6-log's own ISO 8601 timestamp
            so even output that lacked a Python-logger timestamp
            (rich banners, third-party libs' raw prints) is
            correlatable in ``current``.
        r   Nzs#!/command/with-contenv sh
# shellcheck shell=sh
: "${HERMES_HOME:=/opt/data}"
log_dir="$HERMES_HOME/logs/gateways/a$  "
if [ "$(id -u)" = 0 ]; then
  s6-setuidgid hermes mkdir -p "$log_dir"
  s6-setuidgid hermes rm -f "$log_dir/lock"
else
  mkdir -p "$log_dir"
  rm -f "$log_dir/lock"
fi
[ "$(id -u)" = 0 ] || exec s6-log 1 n10 s1000000 T "$log_dir"
exec s6-setuidgid hermes s6-log 1 n10 s1000000 T "$log_dir"
)rö   rú   )r4   rö   Úprofs      r   Ú_render_log_runz S6ServiceManager._render_log_runà  sA   € ðV 	ˆˆˆØ�{Š{˜7Ñ#Ô#ˆðMð 48ðMð Mð Mð	
r   Úaction_flagÚaction_labelr   c           	     ó¦  — ddl }| j        |z  }|                     ¦   «         sG|                     t          ¦  «        r|t          t          ¦  «        d…         n|}t          |¦  «        ‚	 |                     t          › d�|t          |¦  «        gdddddd¬¦  «         dS # |j
        $ r%}t          |||j        |j        pd	¬
¦  «        |‚d}~ww xY w)ug  Shared lifecycle dispatch for start / stop / restart.

        Translates the two failure modes operators care about into
        named errors:

        * ``GatewayNotRegisteredError`` â€” the service directory at
          ``<scandir>/<name>/`` doesn't exist. ``s6-svc`` would
          exit non-zero with a fairly opaque message; we pre-empt
          it with a clear "no such gateway 'X'" tied to the profile
          name (without the ``gateway-`` prefix).
        * ``S6CommandError`` â€” anything else (EACCES on the
          supervise control FIFO, timeout, etc.). Carries the
          subprocess return code and stderr so callers can render
          them inline.

        ``action_flag`` is the ``s6-svc`` flag (``-u`` / ``-d`` /
        ``-t``); ``action_label`` is the human verb (``start`` /
        ``stop`` / ``restart``) used in error messages.
        r   Nú/s6-svcTrS   r¸   é   )ÚcheckÚcapture_outputÚtextrU   ÚerrorsÚtimeoutÚ )rÙ   rè   ré   rê   )Ú
subprocessrí   rY   rœ   r�   r   rä   ÚrunÚ_S6_BIN_DIRr   ÚCalledProcessErrorrç   ré   rê   )r$   r	  r
  r   r  Úservice_dirr4   Úexcs           r   Ú_run_svczS6ServiceManager._run_svc'  s!  € ð( 	ÐÐÐà”l TÑ)ˆØ×!Ò!Ñ#Ô#ð 	5ð
 —?’?Õ#4Ñ5Ô5ð�•SÕ*Ñ+Ô+Ð,Ð,Ô-Ð-àð õ
 ,¨GÑ4Ô4Ð4ð	Ø�NŠNÝÐ(Ð(Ð(¨+µs¸;Ñ7GÔ7GÐHØ¨4°dÀWÐU^Ðhið ñ ô ð ð ð øð Ô,ð 	ð 	ð 	Ý ØØ#Øœ>Ø”zÐ' Rð	ñ ô ð
 ðøøøøð	øøøs   Á+4B! Â!
CÂ+ CÃCc                óT   — |                       dd|¦  «         t          |d¦  «         dS )a$  Bring up a registered service (``s6-svc -u``).

        Raises:
            GatewayNotRegisteredError: no service directory for ``name``.
            S6CommandError: s6-svc exited non-zero for any other reason
                (permission denied on the supervise FIFO, timeout, etc.).
        z-ur%   rr   N©r  r½   r#   s     r   r%   zS6ServiceManager.startU  s0   € ð 	�Š�d˜G TÑ*Ô*Ð*Ý$ T¨9Ñ5Ô5Ð5Ð5Ð5r   ú
int | Nonec                óL  — ddl }	 |                     t          › d�t          | j        |z  ¦  «        gddddd¬¦  «        }n# t
          |j        f$ r Y dS w xY w|j        dk    rdS t          j	        d	|j
        ¦  «        }|r"t          |                     d
¦  «        ¦  «        ndS )a˜  Return the PID of the supervised gateway process, or None.

        Parses ``s6-svstat`` output (``up (pid NNNN) ...``). Used to
        mark an operator-initiated stop with the planned-stop marker so
        the gateway's shutdown handler classifies the incoming SIGTERM
        as intentional rather than an unexpected kill (issue #42675).
        Best-effort: any parse/exec failure returns None.
        r   Nú
/s6-svstatTrS   r¸   r  ©r  r  rU   r  r  z\(pid (\d+)\)é   )r  r  r  r   rí   rX   ÚSubprocessErrorré   ÚreÚsearchÚstdoutr³   Úgroup)r$   r   r  ÚresultÚms        r   Ú_supervised_pidz S6ServiceManager._supervised_pid`  sÍ   € ð 	ÐÐÐð	Ø—^’^ÝÐ+Ð+Ð+­S°´ÀÑ1DÑ-EÔ-EÐFØ#¨$¸ÈÐ\]ð $ñ ô ˆFˆFøõ ˜Ô3Ð4ð 	ð 	ð 	Ø�4�4ð	øøøàÔ Ò!Ð!Ø�4ÝŒIÐ&¨¬Ñ6Ô6ˆØ"#Ð-�s�1—7’7˜1‘:”:‰Œˆ¨Ð-s   †:A ÁAÁAc                óÈ   — |                       |¦  «        }|�#	 ddlm}  ||¦  «         n# t          $ r Y nw xY w|                      dd|¦  «         t          |d¦  «         dS )u¢  Bring down a registered service (``s6-svc -d``).

        Writes a planned-stop marker naming the supervised gateway PID
        BEFORE sending the down command, so the gateway's shutdown
        handler recognises this SIGTERM as an operator-initiated stop
        and persists ``gateway_state=stopped`` (respecting the explicit
        intent). Without the marker, an intentional ``hermes gateway
        stop`` is indistinguishable from the container/s6 SIGTERM sent on
        ``docker restart``; the latter must NOT persist ``stopped`` or
        container_boot refuses to auto-start on the next boot (#42675).
        The marker write is best-effort â€” a failure only means the stop
        is treated as signal-initiated, which is the safe fallback.

        Raises:
            GatewayNotRegisteredError: no service directory for ``name``.
            S6CommandError: s6-svc exited non-zero for any other reason.
        Nr   )Úwrite_planned_stop_markerú-dr(   Ústopped)r)  Úgateway.statusr+  Ú	Exceptionr  r½   )r$   r   Úpidr+  s       r   r(   zS6ServiceManager.stopw  s“   € ð$ ×"Ò" 4Ñ(Ô(ˆØˆ?ðØDÐDÐDÐDÐDÐDà)Ð)¨#Ñ.Ô.Ð.Ð.øÝð ð ð Ø�ðøøøà�Š�d˜F DÑ)Ô)Ð)Ý$ T¨9Ñ5Ô5Ð5Ð5Ð5s   ™+ «
8·8c                óT   — |                       dd|¦  «         t          |d¦  «         dS )zäRestart a registered service (``s6-svc -t`` = SIGTERM).

        Raises:
            GatewayNotRegisteredError: no service directory for ``name``.
            S6CommandError: s6-svc exited non-zero for any other reason.
        ú-tr*   rr   Nr  r#   s     r   r*   zS6ServiceManager.restart”  s0   € ð 	�Š�d˜I tÑ,Ô,Ð,Ý$ T¨9Ñ5Ô5Ð5Ð5Ð5r   r+   c                ó¦   — ddl }|                     t          › d�t          | j        |z  ¦  «        gddddd¬¦  «        }|j        dk    od	|j        v S )
z1True iff ``s6-svstat`` reports the service as up.r   Nr  TrS   r¸   r  r   zup )r  r  r  r   rí   ré   r%  )r$   r   r  r'  s       r   r-   zS6ServiceManager.is_runningž  sn   € àÐÐÐØ—’ÝÐ'Ð'Ð'­¨T¬\¸DÑ-@Ñ)AÔ)AÐBØ d°WÀYÐXYð  ñ 
ô 
ˆð Ô  AÒ%Ð@¨%°6´=Ð*@Ð@r   c                ó   — dS )NTr"   r/   s    r   r0   z.S6ServiceManager.supports_runtime_registration©  s   € Øˆtr   NTr1   r5   r3   c               ó  — ddl }ddl}|                      |¦  «        }|                     ¦   «         rt	          d|›d|› �¦  «        ‚|                     d|j        z   dz   ¦  «        }|                     ¦   «         r|                     |d¬¦  «         |                     d¬	¦  «         	 |d
z   	                    dd¬¦  «         |  
                    ||pi ¦  «        }|dz  }	|	 	                    |d¬¦  «         |	                     d¦  «         |dz  }
|
 	                    |                      ¦   «         d¬¦  «         |
                     d¦  «         |dz  }|                     ¦   «          |dz  }| 	                    |                      |¦  «        d¬¦  «         |                     d¦  «         t          |¦  «         |s|dz                       ¦   «          |                     |¦  «         n&# t"          $ r |                     |d¬¦  «         ‚ w xY w|                     t&          › d�dt)          | j        ¦  «        gddddd¬¦  «        }|j        dk    r5|                     |d¬¦  «         t/          d|j        p|j        › �¦  «        ‚dS )a^  Create the s6 service directory for a profile gateway.

        Triggers ``s6-svscanctl -a`` so s6-svscan picks the new directory
        up immediately.  When *start_now* is ``True`` (the default) the
        service starts immediately; when ``False`` a ``down`` marker file
        is written so s6-supervise leaves the service stopped until the
        user explicitly runs ``hermes -p <profile> gateway start``.

        Raises:
            ValueError: if the profile name is invalid or the service
                directory already exists.
            RuntimeError: if ``s6-svscanctl`` fails.
        r   Nzprofile gateway z already registered at ú.r§   T©Úignore_errors)rÃ   ra   zlongrun
rS   rT   r  rÎ   ÚfinishrÐ   Údownú/s6-svscanctlz-ar¸   r  r   zs6-svscanctl failed: )Úshutilr  rð   r®   r   Ú	with_namer   ÚrmtreerÅ   r¶   r   rÆ   r  r  rÕ   ÚtouchÚrenamer/  r  r  r   rí   ré   r”   rê   r%  )r$   r4   r2   r3   r<  r  r¾   Útmp_dirÚ
run_scriptÚrun_pathÚfinish_pathÚ
log_subdirÚlog_runr'  s                 r   r8   z)S6ServiceManager.register_profile_gateway¬  sæ  € ð( 	ˆˆˆØÐÐÐà×#Ò# GÑ,Ô,ˆØ�>Š>ÑÔð 	ÝØN 7ÐNÐNÀWÐNÐNñô ð ð* ×#Ò# C¨'¬,Ñ$6¸Ñ$?Ñ@Ô@ˆØ�>Š>ÑÔð 	7Ø�MŠM˜'°ˆMÑ6Ô6Ð6Ø�Š˜dˆÑ#Ô#Ð#ð&	Ø�vÑ×)Ò)¨+ÀÐ)ÑHÔHÐHà×0Ò0°¸)¸/ÀrÑJÔJˆJØ ‘ˆHØ×Ò 
°WÐÑ=Ô=Ð=Ø�NŠN˜5Ñ!Ô!Ð!à! HÑ,ˆKØ×"Ò" 4×#=Ò#=Ñ#?Ô#?È'Ð"ÑRÔRÐRØ×Ò˜eÑ$Ô$Ð$ð ! 5™ˆJØ×ÒÑÔÐØ  5Ñ(ˆGØ×Ò˜t×3Ò3°GÑ<Ô<ÀwÐÑOÔOÐOØ�MŠM˜%Ñ Ô Ð õ % WÑ-Ô-Ð-ð ð +Ø˜6Ñ!×(Ò(Ñ*Ô*Ð*à�NŠN˜7Ñ#Ô#Ð#Ð#øÝð 	ð 	ð 	Ø�MŠM˜'°ˆMÑ6Ô6Ð6Øð	øøøð
 —’ÝÐ*Ð*Ð*¨Dµ#°d´lÑ2CÔ2CÐDØ d°WÀYÐXYð  ñ 
ô 
ˆð Ô Ò!Ð!ð �MŠM˜'°ˆMÑ6Ô6Ð6ÝØH¨¬Ð(F¸¼ÐHÐHñô ð ð	 "Ð!s   Â)E G* Ç*#Hc           	     ó  — ddl }ddl}ddl}|                      |¦  «        }|                     ¦   «         sdS |                     t          › d�dt          |¦  «        gdddddd	¬
¦  «         |                     t          › d�dddt          |¦  «        gdddddd	¬
¦  «         |                     t          › d�dt          | j        ¦  «        gdddddd	¬
¦  «         | 	                    d¦  «         | 
                    |d¬¦  «         dS )aô  Stop the profile gateway service and remove its directory.

        Idempotent: absent services are a no-op. Best-effort stop +
        wait-for-down before removal so the running gateway process
        gets a chance to shut down cleanly before its service dir
        disappears.

        Teardown ordering matters: ``s6-svscanctl -an`` is fired
        **before** ``rmtree`` so s6-svscan reaps the supervise child
        process (releasing its handle on ``supervise/lock`` and the
        regular files inside the supervise dir), giving us a clean
        directory to remove. Without the reap-first ordering, the
        rmtree races s6-supervise on a set of root-owned files inside
        the supervise dir and the dir is left half-removed.
        r   Nr  r,  TrS   r¸   r  F)r  r  rU   r  r  r  z
/s6-svwaitz-Dr2  Ú10000é   r;  z-angš™™™™™É?r7  )r<  r  r­   rð   r®   r  r  r   rí   Úsleepr>  )r$   r4   r<  r  r­   r¾   s         r   r;   z+S6ServiceManager.unregister_profile_gateway  sR  € ð  	ˆˆˆØÐÐÐØˆˆˆà×#Ò# GÑ,Ô,ˆØ�~Š~ÑÔð 	ØˆFð 	�ŠÝÐ$Ð$Ð$ d­C°©L¬LÐ9Ø d°WÀYÐXYØð 	ñ 	
ô 	
ð 	
ð 	�ŠÝÐ'Ð'Ð'¨¨t°W½cÀ'¹l¼lÐKØ d°WÀYÐXZØð 	ñ 	
ô 	
ð 	
ð 	�ŠÝÐ*Ð*Ð*¨Eµ3°t´|Ñ3DÔ3DÐEØ d°WÀYÐXYØð 	ñ 	
ô 	
ð 	
ð 	�
Š
�3‰Œˆð 	�Š�g¨TˆÑ2Ô2Ð2Ð2Ð2r   r<   c                ó€  — | j                              ¦   «         sg S g }| j                              ¦   «         D ]†}|j                             d¦  «        rŒ|                     ¦   «         sŒ2|j                             t          ¦  «        sŒR|                     |j        t          t          ¦  «        d…         ¦  «         Œ‡|S )zâReturn the profile names of all currently-registered gateway services.

        Filters the scandir to entries that match the ``gateway-`` prefix.
        Other services (e.g. ``s6-linux-init-shutdownd``) are ignored.
        r6  N)	rí   r®   Úiterdirr   rœ   rY   r�   rù   r   )r$   r™   Úentrys      r   r>   z&S6ServiceManager.list_profile_gatewaysT  s½   € ð Œ|×"Ò"Ñ$Ô$ð 	ØˆIØ ˆØ”\×)Ò)Ñ+Ô+ð 	Að 	AˆEØŒz×$Ò$ SÑ)Ô)ð ØØ—<’<‘>”>ð ØØ”:×(Ò(Õ):Ñ;Ô;ð ØØ�OŠO˜EœJ¥sÕ+<Ñ'=Ô'=Ð'>Ð'>Ô?Ñ@Ô@Ð@Ð@Øˆr   )rí   r   r   r   )r4   r   r   r   )r4   r   r   r   )r4   r   r2   ró   r   r   )r   r   )r	  r   r
  r   r   r   r   r   r?   )r   r   r   r  r@   rA   rB   rC   rD   )rE   rF   rG   rH   r   rI   ÚS6_DYNAMIC_SCANDIRrÞ   rð   rò   Ústaticmethodr   r  r  r  r%   r)  r(   r*   r-   r0   r8   r;   r>   r"   r   r   r“   r“   Y  sÈ  € € € € € € ðð ð  $€DÐ#Ð#Ð#Ñ#à'9ð ð ð ð ð ð
>ð >ð >ð >ð/ð /ð /ð /ð ðV'ð V'ð V'ñ „\ðV'ðp ð
ð 
ð 
ñ „\ð
ð. ðB
ð B
ð B
ñ „\ðB
ðL,ð ,ð ,ð ,ð\	6ð 	6ð 	6ð 	6ð.ð .ð .ð .ð.6ð 6ð 6ð 6ð:6ð 6ð 6ð 6ðAð Að Að Aðð ð ð ð ,0Øðfð fð fð fð fð fðP>3ð >3ð >3ð >3ð@ð ð ð ð ð r   r“   r?   )r   r   rA   )r   r   )r   r   r   r   )r   r   r¤   r   r   r   )r¾   r   r   r   )$rH   Ú
__future__r   r#  Úpathlibr   Útypingr   r   r   r   Úcompiler   r   r   r   rQ   rP   r\   re   ru   r   r•   rN  r�   r£   r½   r  rÈ   rÉ   rÕ   r”   r×   rä   rç   r“   r"   r   r   ú<module>rT     sæ  ððð ð  #Ð "Ð "Ð "Ð "Ð "à 	€	€	€	Ø Ð Ð Ð Ð Ð Ø 7Ð 7Ð 7Ð 7Ð 7Ð 7Ð 7Ð 7Ð 7Ð 7àÐJÔKÐ ð �B”JÐ7Ñ8Ô8Ð ØÐ ð
ð 
ð 
ð 
ð( ð6ð 6ð 6ð 6ð 6�Xñ 6ô 6ñ Ôð6ð@(ð (ð (ð (ðV,ð ,ð ,ð ,ð\ð ð ð ð ñ ô ð ð8ð ð ð ð Ð9ñ ô ð ð80ð 0ð 0ð 0ð 0Ð9ñ 0ô 0ð 0ð,/)ð /)ð /)ð /)ð /)Ð9ñ /)ô /)ð /)ðd@ð @ð @ð @ð> �T˜.Ñ)Ô)Ð ØÐ ðIð Ið Ið Ið*ð ð ð ð\ €ð €Ø€ð@ð @ð @ð @ðF
ð 
ð 
ð 
ð 
ˆlñ 
ô 
ð 
ð
ð 
ð 
ð 
ð 
 ñ 
ô 
ð 
ð&3ð 3ð 3ð 3ð 3�Wñ 3ô 3ð 3ð,Lð Lð Lð Lð Lñ Lô Lð Lð Lð Lr   