
    UAj                    "   S r SSKJr  SSKrSSKrSSKrSSKrSSKrSSKrSSK	J
r
  SSKJr  \" S5      rSrSS jrSS	 jrSS
 jrSS jrSS jrSS jrSS jrSS S jjrSS!S jjrS"S jrSS jrS#S jrS$S jrS%S jrS&S jrS'S jrS'S jrg)(a   Read-only system facts and a safe subprocess helper.

Everything here is strictly read-only: no service is touched, nothing is
written to the server. On non-Linux dev machines functions degrade gracefully
and return None / empty values instead of raising.
    )annotationsN)Any)
get_loggerzdiscovery.system
   c                .   [         R                  " U S   5      (       d  g [        R                  " U SS[        SS9nUR
                  S:w  a  gUR                  $ ! [        R                  [        4 a   n[        R                  SX5         SnAgSnAff = f)zRun a read-only command and return stdout, or None on any failure.

Never raises. Used for inspection commands only (systemctl list, ss, etc.).
Destructive commands must never be passed here.
r   NTF)capture_outputtexttimeoutcheckzcommand failed %s: %s)shutilwhich
subprocessrun_CMD_TIMEOUT
returncodestdoutSubprocessErrorOSErrorlogdebug)cmdresultexcs      :D:\Programs\laragon\www\wolf\agent\src\discovery\system.pyrun_readonlyr      s     <<A 
 !}}&&0 		)34s   ,A A B4BBc                    [         R                  " 5       [         R                  " 5       [         R                   " 5       S.n  [        SSSS9 n0 nU H@  nSU;   d  M  UR	                  5       R                  S5      u  pEnUR	                  S5      X$'   MB     SSS5        S	W;   a  US	   U S
'   UR                  S5      U S'   UR                  S5      U S'   U $ ! , (       d  f       NF= f! [         a     U $ f = f)zDOperating system name/version, reading /etc/os-release when present.)systemreleasepretty_namez/etc/os-releaserutf-8encoding="NPRETTY_NAMEr   ID	distro_id
VERSION_IDdistro_version)platformr   r   openstrip	partitiongetr   )infohandlefieldslinekey_values          r   os_infor7   1   s     //###%((*D
#S7;vF$;$(JJL$:$:3$?MCE"'++c"2FK  < F""("7D"JJt,[!'L!9 K <;  Ks0   C& C9C>C& 
C#C& &
C43C4c                 ,    [         R                  " 5       $ N)r+   r        r   kernelr<   H       r;   c                 ,    [         R                  " 5       $ r9   )r+   machiner:   r;   r   architecturer@   L   r=   r;   c                 >    [         R                  " 5       =(       d    S$ )Nr   )os	cpu_countr:   r;   r   rC   rC   P   s    <<>Qr;   c                     [        SSSS9 n U  HE  nUR                  S5      (       d  M  [        UR                  5       S   5      nUS-  s  sSSS5        $    SSS5        g! , (       d  f       g= f! [        [
        4 a     gf = f)	z9Total RAM in bytes from /proc/meminfo (Linux), else None./proc/meminfor    r!   r"   z	MemTotal:      N)r,   
startswithintsplitr   
ValueError)r1   r3   kibs      r   memory_total_bytesrM   T   s    /39V??;//djjl1o.C:%	 :9 :  :9  Z  s?   A3 A""A"	A3 A"A3 "
A0,A3 0A3 3BBc                 b   0 n  [        SSSS9 n0 nU HF  nUR                  S5      u  pEn[        UR                  5       S   5      S-  X$R	                  5       '   MH     SSS5        S	W;   a  US	   U S
'   SU;   a  US   U S'   U $ ! , (       d  f       N,= f! [
        [        [        4 a     U $ f = f)z.Total/available memory in bytes (best effort).rE   r    r!   r"   :r   rG   NMemTotaltotalMemAvailable	available)r,   r.   rI   rJ   r-   r   rK   
IndexError)usager1   valuesr3   r4   r5   rests          r   memory_usagerX   a   s    E/39VF#~~c2&)$**,q/&:T&Ayy{#  :
 #J/E'NV#!'!7E+ L :9 Z, Ls)   B AB$B 
BB B.-B.c                d     [         R                  " U 5      R                  $ ! [         a     g f = fr9   )r   
disk_usagerQ   r   )paths    r   disk_total_bytesr\   s   s0      &,,, s   " 
//c                     [         R                  " U 5      nUR                  UR                  UR                  S.$ ! [
         a    0 s $ f = f)N)rQ   usedfree)r   rZ   rQ   r^   r_   r   )r[   rU   s     r   rZ   rZ   z   sD    !!$'ejj%**MM 	s   9< A
Ac                 p     [        [        R                  " 5       5      $ ! [        [        4 a    / s $ f = fr9   )listrB   
getloadavgr   AttributeErrorr:   r;   r   load_averagerd      s1    BMMO$$^$ 	s     55c                 ,    [         R                  " 5       $ r9   )socketgethostnamer:   r;   r   hostnamerh      s    r;   c                      [        SSSS9 n [        U R                  5       R                  5       S   5      sS S S 5        $ ! , (       d  f       g = f! [        [
        [        4 a     g f = f)Nz/proc/uptimer    r!   r"   r   )r,   floatreadlinerJ   r   rK   rT   )r1   s    r   uptime_secondsrl      sV    .#8F*002156 988Z, s-   A *A	A 
AA A A*)A*c                     [         R                  " U 5      nUR                  S:H  =(       a2    UR                  =(       d    UR
                  =(       d    UR                  $ ! [         a     gf = f)zTrue only for RFC1918, loopback and link-local IPv4 addresses.

Public addresses must never be labelled private, so we classify with the
stdlib ipaddress module rather than naive prefix checks.
F   )	ipaddress
ip_addressrK   version
is_privateis_loopbackis_link_local)addrips     r   _is_private_ipv4rw      sX    !!$' ::?T S S2CSCST  s   A   
A-,A-c                 ,   [        5       n  [        R                  " [        R                  " 5       S[        R                  5       H  nU R                  US   S   5        M      [        R                  " [        R                  [        R                  5      nUR                  S5        U R                  UR                  5       S   5        UR                  5         [        S U  5       5      $ ! [        R                   a     Nf = f! [         a     N8f = f)zDBest-effort list of local *private* IPv4 addresses (RFC1918 et al.).Nrn   r   )z10.255.255.255rF   c              3  H   #    U H  n[        U5      (       d  M  Uv   M     g 7fr9   )rw   ).0rv   s     r   	<genexpr>private_ips.<locals>.<genexpr>   s     ;s&6r&:""ss   "	")setrf   getaddrinforg   AF_INETaddgaierror
SOCK_DGRAMconnectgetsocknamecloser   sorted)ipsr0   socks      r   private_ipsr      s    EC&&v'9'9';T6>>RDGGDGAJ S
}}V^^V->->?*+  "1%&

 ;s;;; ??   s%   AC, $A6D ,DD
DDc                     g)zPlaceholder for v1. The control plane already sees the source IP, and
discovery must stay read-only/local, so we do not call an external echo
service here. TODO(prod): resolve via panel-side observation or opt-in.Nr:   r:   r;   r   	public_ipr      s     r;   c                     [        / SQ5      n / nU (       d  U$ U R                  5        HB  nUR                  5       n[        U5      S:  d  M$  UR	                  US   US   US   S.5        MD     U$ )z5Parse `ip -o addr` output when available (read-only).)rv   z-oru   rn   rF         )	interfacefamilyaddress)r   
splitlinesrJ   lenappend)output
interfacesr3   partss       r   network_interfacesr      sp    ./F')J!!#

u:?E!HaUZ[\U]^_ $ r;   c                     [        SS/5      n / nU (       d  U$ U R                  5        HQ  nUR                  5       n[        U5      S:  d  M$  US   nUR	                  SS5      S   nUR                  XES.5        MS     U$ )	zGListening TCP sockets via `ss -tlnH` (read-only). Empty if unavailable.ssz-tlnHrn   r   rO   rF   )localport)r   r   rJ   r   rsplitr   )r   portsr3   r2   r   r   s         r   
open_portsr      s{    4/*F"$E!!#v;!1IE<<Q'+DLL578 $ Lr;   )r   	list[str]return
str | None)r   zdict[str, Any])r   str)r   rI   )r   
int | None)r   dict[str, int])/)r[   r   r   r   )r[   r   r   r   )r   zlist[float])r   zfloat | None)ru   r   r   bool)r   r   )r   r   )r   zlist[dict[str, Any]]) __doc__
__future__r   ro   rB   r+   r   rf   r   typingr   loggerr   r   r   r   r7   r<   r@   rC   rM   rX   r\   rZ   rd   rh   rl   rw   r   r   r   r   r:   r;   r   <module>r      s    #  	      #$0.
$ 
U<(
r;   