+
    jHàj .  ã                  óÆ  € R t ^ RIHt ^ RIHt ^ RIt^ RIt^ RIHt ^ RI	t	^ RI
t
^ RIHt  ! R R]4      tR R	 ltR
 R lt]P"                  ]P$                  ,          ]! ]R^ 4      ,          tR R ltR R ltR R ltR R ltR R ltR R ltR R ltR R lt ! R R4      t]! 4       t]
P>                  ! 4       t R R  lt!R! R" lt"R# R$ lt#]R%R/R& R' ll4       t$R# )(a�  Nonblocking, all-or-nothing reservations of canonical profile homes.

A held lock inode is never removed: unlinking it creates a second owner. The one exception is a
stale inode this user cannot write (left by a gateway that ran as root, #42685) that a flock through
a read-only descriptor proves unheld; it is replaced once, and every winner checks that the path
still names the inode it locked.
)Úannotations)ÚcontextmanagerN)ÚPath)Úmake_user_dirc                  ó   € ] tR t^tRtR# )ÚOwnershipConflict© N)Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__static_attributes__r   ó    Ú>/home/ubuntu/.hermes/hermes-agent/gateway/runtime_ownership.pyr   r      s   † Ûr   r   c               ó    € V ^8„  d   QhRRRR/# )é   Úhomer   Úreturnr   )Úformats   "r   Ú__annotate__r      s   € ÷ Jñ J˜ð J $ñ Jr   c           	     ó®   € \        \        P                  P                  \	        \        V 4      P                  4       P                  4       4      4      4      # ©N)r   ÚosÚpathÚnormcaseÚstrÚ
expanduserÚresolve)r   s   &r   Úcanonical_homer      s7   € Ü”—‘× Ñ ¤¤T¨$£Z×%:Ñ%:Ó%<×%DÑ%DÓ%FÓ!GÓHÓIÐIr   c               ó    € V ^8„  d   QhRRRR/# )r   ÚfdÚintr   ÚNoner   )r   s   "r   r   r      s   € ÷ ñ ˜#ð  $ñ r   c                ó0  € \         P                  ! V 4      p\         P                  R8X  d4   \        P                  ! VP
                  4      '       g   \        R4      hR# \        P                  ! VP
                  4      '       d%   VP                  \         P                  ! 4       8w  d   \        R4      h\        P                  ! VP
                  4      ^?,          '       d6   VP                  ^8w  d   \        R4      h\         P                  ! V R4       R# R# )aÌ  Validate an open ``gateway.lock`` descriptor and narrow an upgraded lock to 0600.

Main's ``acquire_gateway_runtime_lock`` created the inode with ``open(path, "a+")`` under the
umask (0644 on most hosts), and lock inodes are never replaced, so an upgraded home keeps that
mode forever and discovery refuses it. Only a regular, single-link inode this user owns is
chmodded, through the already-open NOFOLLOW descriptor (no path race); anything else is refused.
Úntz!unsafe gateway lock owner or typeNzunsafe gateway lock link counté€  )r   ÚfstatÚnameÚstatÚS_ISREGÚst_modeÚPermissionErrorÚst_uidÚgetuidÚS_IMODEÚst_nlinkÚfchmod)r    Úinfos   & r   Útighten_lock_moder2      s±   € ô �8Š8�B‹<€DÜ	‡w�w�$„Ü�|Š|˜DŸL™L×)Ò)Ü!Ð"EÓFÐFÙÜ�<Š<˜Ÿ™×%Ò%¨¯©¼¿	º	»Ô)CÜÐAÓBÐBÜ‡|‚|�D—L‘LÓ! E×)Ô)Ø�=‰=˜AÔÜ!Ð"BÓCÐCÜ
�	Š	�"�eÖñ *r   Ú
O_NOFOLLOWc               ó$   € V ^8„  d   QhRRRRRR/# )r   r   r   Úflagsr!   r   r   )r   s   "r   r   r   3   s!   € ÷ ñ ˜ð  Sð ¨Sñ r   c                ó˜   € \         P                  ! WR 4      p \        V4       V#   \         d    \         P                  ! T4       h i ; i)r%   )r   Úopenr2   ÚBaseExceptionÚclose)r   r5   r    s   && r   Ú_open_checkedr:   3   sE   € Ü	�Š�˜eÓ	$€BðÜ˜"Ôð €Iøô ô Ü
�Š�ŒØðús	   ™& ¦#A	c               ó    € V ^8„  d   QhRRRR/# ©r   r   r   r   Úboolr   )r   s   "r   r   r   =   s   € ÷ ñ ˜Tð  dñ r   c           	     ó´  € ^ RI HpHp \        P                  R8X  d   R# \        P
                  ! V 4      p\        P                  ! VP                  4      '       d   VP                  ^8w  d   R# V! V! V P                  R4      4      4      e   R#  \        P                  ! V \        P                  \        \        R^ 4      ,          \        P                  ,          4      p^ RIp \%        \        P&                  ! T4      T4      '       g    \        P(                  ! T4       R#  TP+                  YEP,                  TP.                  ,          4       \3        Y4      \        P(                  ! T4       #   \         d    \        \!        T 4      4      Rhi ; i  \0         d     \        P(                  ! T4       R# i ; i  \        P(                  ! T4       i ; i)aÌ  Unlink a ``gateway.lock`` this user cannot write (another uid's) once a flock taken through a
read-only descriptor proves it unheld. Symlinks, hardlinks, non-regular files, a home whose
``gateway.pid`` names a live process and anything not provably unheld are left in place (False).
An inode this user cannot even open is refused with the recovery command: an unheld snapshot
(``/proc/locks``) does not stop a holder locking it between the read and the unlink.)Ú_live_pid_from_recordÚ_read_pid_recordr$   Fúgateway.pidNr3   )Úgateway.statusr?   r@   r   r'   Úlstatr(   r)   r*   r/   Ú	with_namer7   ÚO_RDONLYÚgetattrÚ
O_NONBLOCKr+   Úforeign_lock_recoveryÚfcntlÚ_same_inoder&   r9   ÚflockÚLOCK_EXÚLOCK_NBÚOSErrorÚ_unlink_if_same)r   r?   r@   r1   ÚproberI   s   &     r   Ú_unlink_stale_lockrQ   =   sH  € ÷ GÜ	‡w�w�$„ÙÜ�8Š8�D‹>€DÜ�<Š<˜Ÿ™×%Ò%¨¯©¸!Ô);ÙÙÑ-¨d¯n©n¸]Ó.KÓLÓMÒYÙðEÜ—’˜œbŸk™k¬G´B¸ÀaÓ,HÕHÌ2Ï=É=ÕXÓYˆó ð
Üœ2Ÿ8š8 E›?¨D×1Ò1Øô 	�Š�Žð	Ø�K‰K˜Ÿ}™}¨u¯}©}Õ<Ô=ô ˜tÓ*ä
�Š��øô ô EÜÔ3°DÓ9Ó:ÀÐDðEûô ô 	Øô 	�Š�Žð	ûô
 	�Š��ús=   ÂAE3 Ã&&F? Ä&,F Å
F? Å3!FÆF<Æ"F? Æ;F<Æ<F? Æ?Gc               ó    € V ^8„  d   QhRRRR/# )r   r   r   r   r   r   )r   s   "r   r   r   ]   s   € ÷ kñ k ð k¨ñ kr   c                ó   € R V  RV  RV  R2# )zforeign_stale_lock: zj is not readable by this user (left by a gateway run as root?) and cannot be proven unheld. If `sudo lsof z!` lists no process, run `sudo rm z`.r   ©r   s   &r   rH   rH   ]   s+   € Ø" 4 &ð ):Ø:>¸Ð?`ÐaeÐ`fÐfhðjð kr   c               ó    € V ^8„  d   QhRRRR/# )r   r   r   r   ú
str | Noner   )r   s   "r   r   r   b   s   € ÷ ñ  4ð ¨Jñ r   c                ó"  €  \         P                  ! \         P                  ! V \         P                  \	        \         R^ 4      ,          \         P
                  ,          4      4       R#   \         d    \        T 4      u # \         d     R# i ; i)zRThe recovery a client prints for a ``gateway.lock`` it cannot even open read-only.r3   N)	r   r9   r7   rE   rF   rG   r+   rH   rN   rT   s   &r   Úunreadable_lock_recoveryrX   b   sh   € ðÜ
�Š”—’˜œrŸ{™{¬W´R¸ÀqÓ-IÕIÌBÏMÉMÕYÓZÔ[ñ
 øô	 ô +Ü$ TÓ*Ò*Üô ØÙðús   ‚A$A( Á(BÂ BÂ	BÂBc               ó    € V ^8„  d   QhRRRR/# r<   r   )r   s   "r   r   r   m   s   € ÷ ñ ˜$ð ¨ñ r   c                ó‚   € \        \        P                  ! V 4      V4      '       g   R # \        P                  ! V 4       R# )FT)rJ   r   rC   Úunlink)r   r1   s   &&r   rO   rO   m   s(   € Ü”r—x’x “~ t×,Ò,ÙÜ‡I‚Iˆd„OÙr   c               ó   € V ^8„  d   QhRR/# )r   r   r=   r   )r   s   "r   r   r   t   s   € ÷ Fñ F ñ Fr   c                ód   € V P                   V P                  3VP                   VP                  38H  # r   )Úst_devÚst_ino)ÚleftÚrights   &&r   rJ   rJ   t   s'   € Ø�K‰K˜Ÿ™Ð%¨%¯,©,¸¿¹Ð)EÑEÐEr   c               ó    € V ^8„  d   QhRRRR/# )r   r   r   r   r!   r   )r   s   "r   r   r   x   s   € ÷ ,ñ ,�Tð ,˜cñ ,r   c                óŠ   €  \        V \        4      #   \         d    \        T 4      '       g   h  Mi ; i\        T \        4      # )zRDescriptor for *path*, replacing a provably stale inode this user cannot use once.)r:   Ú_LOCK_FLAGSr+   rQ   rT   s   &r   Ú
_open_lockre   x   sE   € ðÜ˜T¤;Ó/Ð/øÜô Ü! $×'Ò'Øñ (ðúô ˜œ{Ó+Ð+s   ‚ ’2±2c               ó$   € V ^8„  d   QhRRRRRR/# )r   r   r   r    r!   r   r=   r   )r   s   "r   r   r   ƒ   s!   € ÷ ñ �tð  ð ¨ñ r   c                ó¾   € \         P                  R8X  d   R#  \        \         P                  ! V 4      \         P                  ! V4      4      #   \
         d     R# i ; i)zVTrue when *path* is still the inode *fd* locked (a stale-lock replacement unlinks it).r$   TF)r   r'   rJ   rC   r&   ÚFileNotFoundError)r   r    s   &&r   Ú_still_namesri   ƒ   sD   € ä	‡w�w�$„ÙðÜœ2Ÿ8š8 D›>¬2¯8ª8°B«<Ó8Ð8øÜô Úðús   ™3A ÁAÁAc                  óv   € ] tR t^�tR t]R R l4       tRR/R R lltR R	 ltR
 R lt	R R lt
R R ltRtR# )ÚProfileOwnershipc                	óV   € / V n         . V n        \        P                  ! 4       V n        R # r   )Ú_handlesÚ_writersÚ	threadingÚRLockÚ_mutex©Úselfs   &r   Ú__init__ÚProfileOwnership.__init__Ž   s   € Ø,.ˆŒØ02ˆŒÜ—o’oÓ'ˆŽr   c               ó   € V ^8„  d   QhRR/# )r   r   ztuple[Path, ...]r   )r   s   "r   r   ÚProfileOwnership.__annotate__”   s   € ÷ (ñ (Ð'ñ (r   c                	ó’   € V P                   ;_uu_ 4        \        V P                  4      uuR R R 4       #   + '       g   i     R # ; ir   )rq   Útuplerm   rr   s   &r   ÚhomesÚProfileOwnership.homes“   s#   € à�[�[‹[Ü˜Ÿ™Ó'÷ �[�[‹[ús	   •5µA	ÚpurposeNc               ó    € V ^8„  d   QhRRRR/# )r   r|   rV   r   r"   r   )r   s   "r   r   rw   ˜   s   € ÷ !ñ !¨ð !¸tñ !r   c          
     óö  € ^ RI HpHp V P                  ;_uu_ 4        . p \	        V Uu0 uF  p\        V4      kK  	  up4       EF:  pWpP                  9   d   K  \        VRRRR7       VR,          p\        V4      p	\        P                  ! V	RRR7      p
 V! V
4      '       d    \        WŠP                  4       4      '       g   \        R	V 24      h/ V! 4       CR
\        V4      /CpV'       d   W+R&   V
P                  ^ 4       V
P!                  4        \"        P$                  ! Wº4       V
P'                  4        \        P(                  ! V
P                  4       4       Y P                  T&   TP/                  T4       EK=  	   RRR4       R# u upi   \*         d    T
P-                  4        h i ; i  \*         d&    \1        T4       F  pT P3                  T4       K  	  h i ; i  + '       g   i     R# ; i)z­``purpose`` marks a maintenance reservation in the lock record, so a contender can tell
a short maintenance holder from a gateway runtime owner (:func:`live_runtime_owner`).)Ú_try_acquire_file_lockÚ_build_pid_recordiÀ  T)ÚmodeÚparentsÚexist_okúgateway.lockzr+zutf-8)Úencodingz%Gateway runtime already owns profile Úhermes_homer|   N)rB   r   r€   rq   Úsortedr   rm   r   re   r   Úfdopenri   Úfilenor   r   ÚseekÚtruncateÚjsonÚdumpÚflushÚfsyncr8   r9   ÚappendÚreversedÚrelease)rs   rz   r|   r   r€   ÚaddedÚpr   r   r    ÚhandleÚrecords   &&$         r   ÚreserveÚProfileOwnership.reserve˜   s”  € ÷ 	MØ�[�[‹[ØˆEðÜ"¹uÓ#E¹u¸!¤N°1Ö$5¹uÑ#E×F�DØŸ}™}Ô,Ù Ü! $¨U¸DÈ4ÕPØ .Õ0�DÜ# DÓ)�BÜŸYšY r¨4¸'ÔB�FðÙ5°f×=Ò=Ä\ÐRV×XeÑXeÓXg×EhÒEhÜ"3Ð6[Ð\`Ð[aÐ4bÓ"cÐcØ!RÑ$5Ó$7Ð!R¸ÌÈDË	Ñ!R˜ß"Ø07 9Ñ-ØŸ™ AœØŸ™Ô)ÜŸ	š	 &Ô1ØŸ™œÜŸš §¡£Ô1ð +1—M‘M $Ñ'Ø—L‘L ×&ò- G÷ ‰[ùò $Føô$ )ô ØŸ™œØðûô
 !ô Ü$ UžO�DØ—L‘L Ö&ñ ,àðú÷5 �[ˆ[úsF   �G'¡	F4ªF¾AF4ÂCFÅ $F4ÆF4ÆF1	Æ1F4Æ40G$Ç$G'Ç'G8	c               ó    € V ^8„  d   QhRRRR/# ©r   r   r   r   r=   r   )r   s   "r   r   rw   »   s   € ÷ 9ñ 9˜ð 9 $ñ 9r   c                	ó˜   € V P                   ;_uu_ 4        \        V4      V P                  9   uuR R R 4       #   + '       g   i     R # ; ir   )rq   r   rm   ©rs   r   s   &&r   ÚownsÚProfileOwnership.owns»   s(   € Ø�[�[‹[Ü! $Ó'¨4¯=©=Ñ8÷ �[�[‹[ús	   •8¸A		c               ó    € V ^8„  d   QhRRRR/# )r   r   r   r   r"   r   )r   s   "r   r   rw   ¿   s   € ÷ ñ ˜Dð  Tñ r   c                	óö   € ^ RI Hp V P                  ;_uu_ 4        V P                  P	                  \        V4      R4      pVe   V! V4       VP                  4        RRR4       R#   + '       g   i     R# ; i)é    )Ú_release_file_lockN)rB   r¢   rq   rm   Úpopr   r9   )rs   r   r¢   r•   s   &&  r   r’   ÚProfileOwnership.release¿   sL   € Ý5Ø�[�[‹[Ø—]‘]×&Ñ&¤~°dÓ';¸TÓBˆFØÒ!Ù" 6Ô*Ø—‘”÷	 �[�[Š[ús   ›AA'Á'A8	c               ó    € V ^8„  d   QhRRRR/# )r   Úthreadzthreading.Threadr   r"   r   )r   s   "r   r   rw   Ç   s   € ÷ ñ Ð#3ð ¸ñ r   c                ó¾   € V P                   ;_uu_ 4        V P                  P                  V4       VP                  4        RRR4       R#   + '       g   i     R# ; i)zHRegister before starting so partial startup cannot forget a live writer.N)rq   rn   r�   Ústart)rs   r¦   s   &&r   Ústart_writerÚProfileOwnership.start_writerÇ   s2   € à�[�[‹[Ø�M‰M× Ñ  Ô(Ø�L‰LŒN÷ �[�[Š[ús   •,AÁA	c               ó   € V ^8„  d   QhRR/# )r   r   r"   r   )r   s   "r   r   rw   Í   s   € ÷ #ñ #�tñ #r   c                	ó¨  € V P                   ;_uu_ 4        \        ;QJ d&    R  V P                   4       F  '       g   K   RM	  RM! R  V P                   4       4      '       d    RRR4       R# V P                  P                  4        \	        V P
                  4       F  pV P                  V4       K  	  RRR4       R#   + '       g   i     R# ; i)c              3  ó@   "  € T F  qP                  4       x € K  	  R # 5ir   )Úis_alive)Ú.0r¦   s   & r   Ú	<genexpr>Ú)ProfileOwnership.close.<locals>.<genexpr>Ñ   s   é € ÐA±=¨—?‘?×$Ð$³=ùs   ‚TFN)rq   Úanyrn   Úclearr‘   rz   r’   rœ   s   & r   r9   ÚProfileOwnership.closeÍ   s}   € Ø�[�[‹[÷ ‹sÑA°4·=²=ÓA�s�sŠsÑA°4·=²=ÓA×AÒAØ÷	 ‰[ð
 �M‰M×ÑÔ!Ü  §¡Ö,�Ø—‘˜TÖ"ñ -÷ �[�[Š[ús   •'C ¿$C Á$C Á/AC Ã C	)rm   rq   rn   )r	   r
   r   r   rt   Úpropertyrz   r—   r�   r’   r©   r9   r   r   r   r   rk   rk   �   sA   † ò(ð
 ô(ó ð(ð!°d÷ !õF9õõ÷#ñ #r   rk   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r   r   r|   r   r   zdict | Noner   )r   s   "r   r   r   Ü   s!   € ÷ ñ ˜Tð ¨Cð °Kñ r   c                óš   € ^ RI HpHp V! \        V 4      R,          4      pV'       d!   VP	                  R4      V8w  g   V! V4      f   R# V# )zóThe live holder record of *home*'s ``gateway.lock`` when it is a ``purpose`` maintenance
reservation (see :meth:`ProfileOwnership.reserve`), else None. Advisory only: it names the
holder for a refusal or a short wait, it never grants the lock.©r?   Ú_read_gateway_lock_recordr„   r|   N)rB   r?   r¹   r   Úget)r   r|   r?   r¹   r–   s   &&   r   Úmaintenance_holderr»   Ü   sB   € ÷ PÙ&¤~°dÓ';¸nÕ'LÓM€Fß�V—Z‘Z 	Ó*¨gÔ5Ñ9NÈvÓ9VÒ9^ÙØ€Mr   c               ó    € V ^8„  d   QhRRRR/# rš   r   )r   s   "r   r   r   ç   s   € ÷ ?ñ ? $ð ?¨4ñ ?r   c                ót   € ^ RI HpHp V! \        V 4      R,          4      pV'       * ;'       g    V! V4      RJ # )a{  True when *home*'s ``gateway.lock`` record names no live holder yet: empty, torn, or a dead
previous holder's. :meth:`ProfileOwnership.reserve` takes the flock BEFORE it writes the record,
so a contender that loses the flock in that window cannot tell maintenance from a gateway; it
must retry (bounded) instead of reporting a runtime owner. Advisory only, never grants the lock.r¸   r„   N)rB   r?   r¹   r   )r   r?   r¹   r–   s   &   r   Úlock_record_unpublishedr¾   ç   s7   € ÷
 PÙ&¤~°dÓ';¸nÕ'LÓM€FØŒ:×>Ð>Ñ.¨vÓ6¸$Ð>Ð>r   c               ó    € V ^8„  d   QhRRRR/# )r   r   r   r   z
int | Noner   )r   s   "r   r   r   ñ   s   € ÷ 	Nñ 	N˜Tð 	N jñ 	Nr   c                óÞ   € ^ RI HpHpHp \	        V 4      p V! V R,          4      pV'       d	   V! V4      MRpVe   VP                  R4      '       d   R# V! V R,          RR7      ;'       g    T# )z¨PID of a live runtime owner of *home* (a gateway, or a reservation that is not marked as
maintenance), else None. A purpose-marked maintenance holder is never an owner.)r?   r¹   Úget_running_pidr„   Nr|   rA   F)Úcleanup_stale)rB   r?   r¹   rÁ   r   rº   )r   r?   r¹   rÁ   r–   Úlives   &     r   Úlive_runtime_ownerrÄ   ñ   sd   € ÷ aÑ`Ü˜$Ó€DÙ& t¨nÕ'<Ó=€Fß,2Ñ  Ô(¸€DØÒ˜FŸJ™J y×1Ò1ÙÙ˜4 -Õ/¸uÔE×MÐMÈÐMr   r|   c               ó   € V ^8„  d   QhRR/# )r   r|   rV   r   )r   s   "r   r   r   þ   s   € ÷ &ñ &¨Zñ &r   c             #  óä  "  € \        \        RR4      pVRJ ;'       g     V^ ,          \        P                  ! 4       8g  pVe   V'       g
   V^,          M	\	        4       p\        VP                  4      p VP                  WR7       V'       d!   \        P                  ! 4       V3\        n        Rx €  \        TP                  4       F  pYu9  g   K  TP                  T4       K  	  T'       d   R\        n        R# R#   \         d   p\        RT R24      ThRp?ii ; i  \        TP                  4       F  pYu9  g   K  TP                  T4       K  	  T'       d   R\        n        i i ; i5i)a¯  Reserve the authority's exact lock before touching managed state.

This is not an owner-presence check: the reservation remains held through
publication, excluding startup even before its PID/control socket exists.
Only nested synchronous maintenance in this thread may reuse a reservation;
being the runtime owner (even in this process) never grants maintenance.
Lock inodes must not be restored, removed, or replaced by callers.
ÚstateN)r|   zExclusive maintenance refused: z). Drain and stop the gateway, then retry.)rF   Ú_maintenancer   Úgetpidrk   Úsetrz   r—   rÇ   r   r‘   r’   )rz   r|   rÇ   Ú	outermostÚownerÚpreviousÚexcr   s   &$      r   Úexclusive_maintenancerÏ   ý   s&  é € ô ”L '¨4Ó0€EØ˜�×8Ð8  q¥¬R¯YªY«[Ñ!8€IØÒ)·)ˆE�!ŽHÔAQÓAS€EÜ�5—;‘;Ó€Hð&Ø�‰�eˆÔ-ßÜ"$§)¢)£+¨uÐ!5ŒLÔÜô ˜UŸ[™[Ö)ˆDØÖ#Ø—‘˜dÖ#ñ *÷ Ø!%ŒLÖñ øô ô ÜØ-¨c¨UÐ2[Ð\ó
àð	ûðûô
 ˜UŸ[™[Ö)ˆDØÖ#Ø—‘˜dÖ#ñ *÷ Ø!%ŒLÕð üsT   ‚AE0Á(E0Á4>C? Â3E0ÃE0Ã0E0Ã?DÄ
DÄDÄD" Ä"E-ÅE-Å E-Å-E0)%Ú__doc__Ú
__future__r   Ú
contextlibr   rŒ   r   Úpathlibr   r(   ro   Úhermes_dir_aclr   ÚRuntimeErrorr   r   r2   ÚO_RDWRÚO_CREATrF   rd   r:   rQ   rH   rX   rO   rJ   re   ri   rk   Úprocess_ownershipÚlocalrÈ   r»   r¾   rÄ   rÏ   r   r   r   Ú<module>rÚ      s×   ðñõ #å %Û Û 	Ý Û Û Ý (ô	˜ô 	õJõð* �i‰i˜"Ÿ*™*Õ$¡w¨r°<ÀÓ'CÕC€õõõ@kõ
õõFõ,õ÷H#ñ H#ñV %Ó&Ð Ø�ŠÓ €õõ?õ	Nð ð&¸$ö &ó ò&r   