PNG  IHDRQgAMA a cHRMz&u0`:pQ<bKGDgmIDATxwUﹻ& ^CX(J I@ "% (** BX +*i"]j(IH{~R)[~>h{}gy)I$Ij .I$I$ʊy@}x.: $I$Ii}VZPC)I$IF ^0ʐJ$I$Q^}{"r=OzI$gRZeC.IOvH eKX $IMpxsk.쒷/&r[޳<v| .I~)@$updYRa$I |M.e JaֶpSYR6j>h%IRز if&uJ)M$I vLi=H;7UJ,],X$I1AҒJ$ XY XzI@GNҥRT)E@;]K*Mw;#5_wOn~\ DC&$(A5 RRFkvIR}l!RytRl;~^ǷJj اy뷦BZJr&ӥ8Pjw~vnv X^(I;4R=P[3]J,]ȏ~:3?[ a&e)`e*P[4]T=Cq6R[ ~ޤrXR Հg(t_HZ-Hg M$ãmL5R uk*`%C-E6/%[t X.{8P9Z.vkXŐKjgKZHg(aK9ڦmKjѺm_ \#$5,)-  61eJ,5m| r'= &ڡd%-]J on Xm|{ RҞe $eڧY XYrԮ-a7RK6h>n$5AVڴi*ֆK)mѦtmr1p| q:흺,)Oi*ֺK)ܬ֦K-5r3>0ԔHjJئEZj,%re~/z%jVMڸmrt)3]J,T K֦OvԒgii*bKiNO~%PW0=dii2tJ9Jݕ{7"I P9JKTbu,%r"6RKU}Ij2HKZXJ,妝 XYrP ެ24c%i^IK|.H,%rb:XRl1X4Pe/`x&P8Pj28Mzsx2r\zRPz4J}yP[g=L) .Q[6RjWgp FIH*-`IMRaK9TXcq*I y[jE>cw%gLRԕiFCj-ďa`#e~I j,%r,)?[gp FI˨mnWX#>mʔ XA DZf9,nKҲzIZXJ,L#kiPz4JZF,I,`61%2s $,VOϚ2/UFJfy7K> X+6 STXIeJILzMfKm LRaK9%|4p9LwJI!`NsiazĔ)%- XMq>pk$-$Q2x#N ؎-QR}ᶦHZډ)J,l#i@yn3LN`;nڔ XuX5pF)m|^0(>BHF9(cզEerJI rg7 4I@z0\JIi䵙RR0s;$s6eJ,`n 䂦0a)S)A 1eJ,堌#635RIgpNHuTH_SԕqVe ` &S)>p;S$魁eKIuX`I4춒o}`m$1":PI<[v9^\pTJjriRŭ P{#{R2,`)e-`mgj~1ϣLKam7&U\j/3mJ,`F;M'䱀 .KR#)yhTq;pcK9(q!w?uRR,n.yw*UXj#\]ɱ(qv2=RqfB#iJmmL<]Y͙#$5 uTU7ӦXR+q,`I}qL'`6Kͷ6r,]0S$- [RKR3oiRE|nӦXR.(i:LDLTJjY%o:)6rxzҒqTJjh㞦I.$YR.ʼnGZ\ֿf:%55 I˼!6dKxm4E"mG_ s? .e*?LRfK9%q#uh$)i3ULRfK9yxm܌bj84$i1U^@Wbm4uJ,ҪA>_Ij?1v32[gLRD96oTaR׿N7%L2 NT,`)7&ƝL*꽙yp_$M2#AS,`)7$rkTA29_Iye"|/0t)$n XT2`YJ;6Jx".e<`$) PI$5V4]29SRI>~=@j]lp2`K9Jaai^" Ԋ29ORI%:XV5]JmN9]H;1UC39NI%Xe78t)a;Oi Ҙ>Xt"~G>_mn:%|~ޅ_+]$o)@ǀ{hgN;IK6G&rp)T2i୦KJuv*T=TOSV>(~D>dm,I*Ɛ:R#ۙNI%D>G.n$o;+#RR!.eU˽TRI28t)1LWϚ>IJa3oFbu&:tJ*(F7y0ZR ^p'Ii L24x| XRI%ۄ>S1]Jy[zL$adB7.eh4%%누>WETf+3IR:I3Xה)3אOۦSRO'ٺ)S}"qOr[B7ϙ.edG)^ETR"RtRݜh0}LFVӦDB^k_JDj\=LS(Iv─aTeZ%eUAM-0;~˃@i|l @S4y72>sX-vA}ϛBI!ݎߨWl*)3{'Y|iSlEڻ(5KtSI$Uv02,~ԩ~x;P4ցCrO%tyn425:KMlD ^4JRxSهF_}شJTS6uj+ﷸk$eZO%G*^V2u3EMj3k%)okI]dT)URKDS 7~m@TJR~荪fT"֛L \sM -0T KfJz+nإKr L&j()[E&I ߴ>e FW_kJR|!O:5/2跌3T-'|zX ryp0JS ~^F>-2< `*%ZFP)bSn"L :)+pʷf(pO3TMW$~>@~ū:TAIsV1}S2<%ޟM?@iT ,Eūoz%i~g|`wS(]oȤ8)$ ntu`өe`6yPl IzMI{ʣzʨ )IZ2= ld:5+請M$-ї;U>_gsY$ÁN5WzWfIZ)-yuXIfp~S*IZdt;t>KūKR|$#LcԀ+2\;kJ`]YǔM1B)UbG"IRߊ<xܾӔJ0Z='Y嵤 Leveg)$znV-º^3Ւof#0Tfk^Zs[*I꯳3{)ˬW4Ւ4 OdpbZRS|*I 55#"&-IvT&/윚Ye:i$ 9{LkuRe[I~_\ؠ%>GL$iY8 9ܕ"S`kS.IlC;Ҏ4x&>u_0JLr<J2(^$5L s=MgV ~,Iju> 7r2)^=G$1:3G< `J3~&IR% 6Tx/rIj3O< ʔ&#f_yXJiގNSz; Tx(i8%#4 ~AS+IjerIUrIj362v885+IjAhK__5X%nV%Iͳ-y|7XV2v4fzo_68"S/I-qbf; LkF)KSM$ Ms>K WNV}^`-큧32ŒVؙGdu,^^m%6~Nn&͓3ŒVZMsRpfEW%IwdǀLm[7W&bIRL@Q|)* i ImsIMmKmyV`i$G+R 0tV'!V)֏28vU7͒vHꦼtxꗞT ;S}7Mf+fIRHNZUkUx5SAJㄌ9MqμAIRi|j5)o*^'<$TwI1hEU^c_j?Е$%d`z cyf,XO IJnTgA UXRD }{H}^S,P5V2\Xx`pZ|Yk:$e ~ @nWL.j+ϝYb퇪bZ BVu)u/IJ_ 1[p.p60bC >|X91P:N\!5qUB}5a5ja `ubcVxYt1N0Zzl4]7­gKj]?4ϻ *[bg$)+À*x쳀ogO$~,5 زUS9 lq3+5mgw@np1sso Ӻ=|N6 /g(Wv7U;zωM=wk,0uTg_`_P`uz?2yI!b`kĸSo+Qx%!\οe|އԁKS-s6pu_(ֿ$i++T8=eY; צP+phxWQv*|p1. ά. XRkIQYP,drZ | B%wP|S5`~́@i޾ E;Չaw{o'Q?%iL{u D?N1BD!owPHReFZ* k_-~{E9b-~P`fE{AܶBJAFO wx6Rox5 K5=WwehS8 (JClJ~ p+Fi;ŗo+:bD#g(C"wA^ r.F8L;dzdIHUX݆ϞXg )IFqem%I4dj&ppT{'{HOx( Rk6^C٫O.)3:s(۳(Z?~ٻ89zmT"PLtw䥈5&b<8GZ-Y&K?e8,`I6e(֍xb83 `rzXj)F=l($Ij 2*(F?h(/9ik:I`m#p3MgLaKjc/U#n5S# m(^)=y=đx8ŬI[U]~SцA4p$-F i(R,7Cx;X=cI>{Km\ o(Tv2vx2qiiDJN,Ҏ!1f 5quBj1!8 rDFd(!WQl,gSkL1Bxg''՞^ǘ;pQ P(c_ IRujg(Wz bs#P­rz> k c&nB=q+ؔXn#r5)co*Ũ+G?7< |PQӣ'G`uOd>%Mctz# Ԫڞ&7CaQ~N'-P.W`Oedp03C!IZcIAMPUۀ5J<\u~+{9(FbbyAeBhOSܳ1 bÈT#ŠyDžs,`5}DC-`̞%r&ڙa87QWWp6e7 Rϫ/oY ꇅ Nܶըtc!LA T7V4Jsū I-0Pxz7QNF_iZgúWkG83 0eWr9 X]㾮݁#Jˢ C}0=3ݱtBi]_ &{{[/o[~ \q鯜00٩|cD3=4B_b RYb$óBRsf&lLX#M*C_L܄:gx)WΘsGSbuL rF$9';\4Ɍq'n[%p.Q`u hNb`eCQyQ|l_C>Lb꟟3hSb #xNxSs^ 88|Mz)}:](vbۢamŖ࿥ 0)Q7@0=?^k(*J}3ibkFn HjB׻NO z x}7p 0tfDX.lwgȔhԾŲ }6g E |LkLZteu+=q\Iv0쮑)QٵpH8/2?Σo>Jvppho~f>%bMM}\//":PTc(v9v!gոQ )UfVG+! 35{=x\2+ki,y$~A1iC6#)vC5^>+gǵ@1Hy٪7u;p psϰu/S <aʸGu'tD1ԝI<pg|6j'p:tպhX{o(7v],*}6a_ wXRk,O]Lܳ~Vo45rp"N5k;m{rZbΦ${#)`(Ŵg,;j%6j.pyYT?}-kBDc3qA`NWQū20/^AZW%NQ MI.X#P#,^Ebc&?XR tAV|Y.1!؅⨉ccww>ivl(JT~ u`ٵDm q)+Ri x/x8cyFO!/*!/&,7<.N,YDŽ&ܑQF1Bz)FPʛ?5d 6`kQձ λc؎%582Y&nD_$Je4>a?! ͨ|ȎWZSsv8 j(I&yj Jb5m?HWp=g}G3#|I,5v珿] H~R3@B[☉9Ox~oMy=J;xUVoj bUsl_35t-(ՃɼRB7U!qc+x4H_Qo֮$[GO<4`&č\GOc[.[*Af%mG/ ňM/r W/Nw~B1U3J?P&Y )`ѓZ1p]^l“W#)lWZilUQu`-m|xĐ,_ƪ|9i:_{*(3Gѧ}UoD+>m_?VPۅ15&}2|/pIOʵ> GZ9cmíتmnz)yߐbD >e}:) r|@R5qVSA10C%E_'^8cR7O;6[eKePGϦX7jb}OTGO^jn*媓7nGMC t,k31Rb (vyܴʭ!iTh8~ZYZp(qsRL ?b}cŨʊGO^!rPJO15MJ[c&~Z`"ѓޔH1C&^|Ш|rʼ,AwĴ?b5)tLU)F| &g٣O]oqSUjy(x<Ϳ3 .FSkoYg2 \_#wj{u'rQ>o;%n|F*O_L"e9umDds?.fuuQbIWz |4\0 sb;OvxOSs; G%T4gFRurj(֍ڑb uԖKDu1MK{1^ q; C=6\8FR艇!%\YÔU| 88m)֓NcLve C6z;o&X x59:q61Z(T7>C?gcļxѐ Z oo-08jہ x,`' ҔOcRlf~`jj".Nv+sM_]Zk g( UOPyεx%pUh2(@il0ݽQXxppx-NS( WO+轾 nFߢ3M<;z)FBZjciu/QoF 7R¥ ZFLF~#ȣߨ^<쩡ݛкvџ))ME>ώx4m#!-m!L;vv#~Y[đKmx9.[,UFS CVkZ +ߟrY٧IZd/ioi$%͝ب_ֶX3ܫhNU ZZgk=]=bbJS[wjU()*I =ώ:}-蹞lUj:1}MWm=̛ _ ¾,8{__m{_PVK^n3esw5ӫh#$-q=A̟> ,^I}P^J$qY~Q[ Xq9{#&T.^GVj__RKpn,b=`żY@^՝;z{paVKkQXj/)y TIc&F;FBG7wg ZZDG!x r_tƢ!}i/V=M/#nB8 XxЫ ^@CR<{䤭YCN)eKOSƟa $&g[i3.C6xrOc8TI;o hH6P&L{@q6[ Gzp^71j(l`J}]e6X☉#͕ ׈$AB1Vjh㭦IRsqFBjwQ_7Xk>y"N=MB0 ,C #o6MRc0|$)ف"1!ixY<B9mx `,tA>)5ػQ?jQ?cn>YZe Tisvh# GMމȇp:ԴVuږ8ɼH]C.5C!UV;F`mbBk LTMvPʍϤj?ԯ/Qr1NB`9s"s TYsz &9S%U԰> {<ؿSMxB|H\3@!U| k']$U+> |HHMLޢ?V9iD!-@x TIî%6Z*9X@HMW#?nN ,oe6?tQwڱ.]-y':mW0#!J82qFjH -`ѓ&M0u Uγmxϵ^-_\])@0Rt.8/?ٰCY]x}=sD3ojަЫNuS%U}ԤwHH>ڗjܷ_3gN q7[q2la*ArǓԖ+p8/RGM ]jacd(JhWko6ڎbj]i5Bj3+3!\j1UZLsLTv8HHmup<>gKMJj0@H%,W΃7R) ">c, xixј^ aܖ>H[i.UIHc U1=yW\=S*GR~)AF=`&2h`DzT󑓶J+?W+}C%P:|0H܆}-<;OC[~o.$~i}~HQ TvXΈr=b}$vizL4:ȰT|4~*!oXQR6Lk+#t/g lԁߖ[Jڶ_N$k*". xsxX7jRVbAAʯKҎU3)zSNN _'s?f)6X!%ssAkʱ>qƷb hg %n ~p1REGMHH=BJiy[<5 ǁJҖgKR*倳e~HUy)Ag,K)`Vw6bRR:qL#\rclK/$sh*$ 6덤 KԖc 3Z9=Ɣ=o>X Ώ"1 )a`SJJ6k(<c e{%kϊP+SL'TcMJWRm ŏ"w)qc ef꒵i?b7b('"2r%~HUS1\<(`1Wx9=8HY9m:X18bgD1u ~|H;K-Uep,, C1 RV.MR5άh,tWO8WC$ XRVsQS]3GJ|12 [vM :k#~tH30Rf-HYݺ-`I9%lIDTm\ S{]9gOڒMNCV\G*2JRŨ;Rҏ^ڽ̱mq1Eu?To3I)y^#jJw^Ńj^vvlB_⋌P4x>0$c>K†Aļ9s_VjTt0l#m>E-,,x,-W)سo&96RE XR.6bXw+)GAEvL)͞K4$p=Ũi_ѱOjb HY/+@θH9޼]Nԥ%n{ &zjT? Ty) s^ULlb,PiTf^<À] 62R^V7)S!nllS6~͝V}-=%* ʻ>G DnK<y&>LPy7'r=Hj 9V`[c"*^8HpcO8bnU`4JȪAƋ#1_\ XϘHPRgik(~G~0DAA_2p|J묭a2\NCr]M_0 ^T%e#vD^%xy-n}-E\3aS%yN!r_{ )sAw ڼp1pEAk~v<:`'ӭ^5 ArXOI驻T (dk)_\ PuA*BY]yB"l\ey hH*tbK)3 IKZ򹞋XjN n *n>k]X_d!ryBH ]*R 0(#'7 %es9??ښFC,ՁQPjARJ\Ρw K#jahgw;2$l*) %Xq5!U᢯6Re] |0[__64ch&_}iL8KEgҎ7 M/\`|.p,~`a=BR?xܐrQ8K XR2M8f ?`sgWS%" Ԉ 7R%$ N}?QL1|-эټwIZ%pvL3Hk>,ImgW7{E xPHx73RA @RS CC !\ȟ5IXR^ZxHл$Q[ŝ40 (>+ _C >BRt<,TrT {O/H+˟Pl6 I B)/VC<6a2~(XwV4gnXR ϱ5ǀHٻ?tw똤Eyxp{#WK qG%5],(0ӈH HZ])ג=K1j&G(FbM@)%I` XRg ʔ KZG(vP,<`[ Kn^ SJRsAʠ5xՅF`0&RbV tx:EaUE/{fi2;.IAwW8/tTxAGOoN?G}l L(n`Zv?pB8K_gI+ܗ #i?ޙ.) p$utc ~DžfՈEo3l/)I-U?aԅ^jxArA ΧX}DmZ@QLےbTXGd.^|xKHR{|ΕW_h] IJ`[G9{).y) 0X YA1]qp?p_k+J*Y@HI>^?gt.06Rn ,` ?);p pSF9ZXLBJPWjgQ|&)7! HjQt<| ؅W5 x W HIzYoVMGP Hjn`+\(dNW)F+IrS[|/a`K|ͻ0Hj{R,Q=\ (F}\WR)AgSG`IsnAR=|8$}G(vC$)s FBJ?]_u XRvύ6z ŨG[36-T9HzpW̞ú Xg큽=7CufzI$)ki^qk-) 0H*N` QZkk]/tnnsI^Gu't=7$ Z;{8^jB% IItRQS7[ϭ3 $_OQJ`7!]W"W,)Iy W AJA;KWG`IY{8k$I$^%9.^(`N|LJ%@$I}ֽp=FB*xN=gI?Q{٥4B)mw $Igc~dZ@G9K X?7)aK%݅K$IZ-`IpC U6$I\0>!9k} Xa IIS0H$I H ?1R.Чj:4~Rw@p$IrA*u}WjWFPJ$I➓/6#! LӾ+ X36x8J |+L;v$Io4301R20M I$-E}@,pS^ޟR[/s¹'0H$IKyfŸfVOπFT*a$I>He~VY/3R/)>d$I>28`Cjw,n@FU*9ttf$I~<;=/4RD~@ X-ѕzἱI$: ԍR a@b X{+Qxuq$IЛzo /~3\8ڒ4BN7$IҀj V]n18H$IYFBj3̵̚ja pp $Is/3R Ӻ-Yj+L;.0ŔI$Av? #!5"aʄj}UKmɽH$IjCYs?h$IDl843.v}m7UiI=&=0Lg0$I4: embe` eQbm0u? $IT!Sƍ'-sv)s#C0:XB2a w I$zbww{."pPzO =Ɔ\[ o($Iaw]`E).Kvi:L*#gР7[$IyGPI=@R 4yR~̮´cg I$I/<tPͽ hDgo 94Z^k盇΄8I56^W$I^0̜N?4*H`237}g+hxoq)SJ@p|` $I%>-hO0eO>\ԣNߌZD6R=K ~n($I$y3D>o4b#px2$yڪtzW~a $I~?x'BwwpH$IZݑnC㧄Pc_9sO gwJ=l1:mKB>Ab<4Lp$Ib o1ZQ@85b̍ S'F,Fe,^I$IjEdù{l4 8Ys_s Z8.x m"+{~?q,Z D!I$ϻ'|XhB)=…']M>5 rgotԎ 獽PH$IjIPhh)n#cÔqA'ug5qwU&rF|1E%I$%]!'3AFD/;Ck_`9 v!ٴtPV;x`'*bQa w I$Ix5 FC3D_~A_#O݆DvV?<qw+I$I{=Z8".#RIYyjǪ=fDl9%M,a8$I$Ywi[7ݍFe$s1ՋBVA?`]#!oz4zjLJo8$I$%@3jAa4(o ;p,,dya=F9ً[LSPH$IJYЉ+3> 5"39aZ<ñh!{TpBGkj}Sp $IlvF.F$I z< '\K*qq.f<2Y!S"-\I$IYwčjF$ w9 \ߪB.1v!Ʊ?+r:^!I$BϹB H"B;L'G[ 4U#5>੐)|#o0aڱ$I>}k&1`U#V?YsV x>{t1[I~D&(I$I/{H0fw"q"y%4 IXyE~M3 8XψL}qE$I[> nD?~sf ]o΁ cT6"?'_Ἣ $I>~.f|'!N?⟩0G KkXZE]ޡ;/&?k OۘH$IRۀwXӨ<7@PnS04aӶp.:@\IWQJ6sS%I$e5ڑv`3:x';wq_vpgHyXZ 3gЂ7{{EuԹn±}$I$8t;b|591nءQ"P6O5i }iR̈́%Q̄p!I䮢]O{H$IRϻ9s֧ a=`- aB\X0"+5"C1Hb?߮3x3&gşggl_hZ^,`5?ߎvĸ%̀M!OZC2#0x LJ0 Gw$I$I}<{Eb+y;iI,`ܚF:5ܛA8-O-|8K7s|#Z8a&><a&/VtbtLʌI$I$I$I$I$I$IRjDD%tEXtdate:create2022-05-31T04:40:26+00:00!Î%tEXtdate:modify2022-05-31T04:40:26+00:00|{2IENDB`Mini Shell

HOME


Mini Shell 1.0
DIR:/opt/imunify360/venv/lib/python3.11/site-packages/defence360agent/simple_rpc/
Upload File :
Current File : //opt/imunify360/venv/lib/python3.11/site-packages/defence360agent/simple_rpc/__init__.py
"""
Simple unix socket RPC server implementation
"""
import asyncio
import functools
import io
import json
import os
import select
import socket
import sys
import time
from contextlib import suppress
from logging import getLogger
from typing import Sequence

from psutil import Process
import sentry_sdk

from defence360agent.api import inactivity
from defence360agent.application import app
from defence360agent.contracts.config import Core, SimpleRpc as Config
from defence360agent.feature_management.exceptions import (
    FeatureManagementError,
)
from defence360agent.internals.auth_protocol import UnixSocketAuthProtocol
from defence360agent.model import tls_check
from defence360agent.model.simplification import run_in_executor
from defence360agent.utils import is_root_user, run_coro
from defence360agent.utils.buffer import LineBuffer
from defence360agent.subsys.panels import hosting_panel
from defence360agent.subsys.panels.base import InvalidTokenException
from defence360agent.subsys import svcctl
from defence360agent.rpc_tools.exceptions import (
    ResponseError,
    ServiceStateError,
    SocketError,
)
from defence360agent.rpc_tools.lookup import Endpoints, UserType
from defence360agent.rpc_tools.utils import (
    is_running,  # noqa: F401
    rpc_is_running,
)
from defence360agent.rpc_tools.validate import ValidationError
from defence360agent.rpc_tools import ERROR, SUCCESS, WARNING


logger = getLogger(__name__)


class RpcServiceState:
    # If need DB and agent should be running
    # e.g. on-demand scan
    RUNNING = "running"

    # Agent should be stopped
    STOPPED = "stopped"

    # It doesn't matter for operation running or stopping the agent
    # if agent is running - using socket, instead of direct communication
    ANY = "any"

    # No need DB and UI interaction
    # preferable for use direct instead any for execution external process
    # e.g. enable/disable plugins/features
    DIRECT = "direct"


async def _execute_request(coro, method):
    try:
        result = await coro
    except ValidationError as e:
        result = {
            "result": WARNING,
            "messages": e.errors,
        }
        result.update(e.extra_data)
        return result
    except (PermissionError, FeatureManagementError) as e:
        msg, *args = e.args
        logger.error(msg, *args)
        return {
            "result": ERROR,
            "messages": [msg % tuple(args)],
        }
    except Exception as e:
        sentry_sdk.capture_exception(e)
        logger.error(
            "Something went wrong while processing %s (%s)", method, str(e)
        )

        return {"result": ERROR, "messages": str(e)}
    else:
        return {"result": SUCCESS, "messages": [], "data": result}


def _apply_middleware(method, user):
    cb = Endpoints.route_to_endpoint
    if isinstance(method, (list, tuple)):
        hashable = tuple(method)
        common = app.MIDDLEWARE.get(None, [])
        specific = app.MIDDLEWARE.get(hashable, [])
        excluded = app.MIDDLEWARE_EXCLUDE.get(hashable, [])
        for mw, users in reversed(common + specific):
            if (user in users) and (mw not in excluded):
                logger.debug("Applying middleware %s", mw.__name__)
                cb = mw(cb)
    return cb


def _find_uds_inodes(socket_path: str) -> Sequence[str]:
    """Find inodes corresponding to the unix domain socket path."""
    with open(
        "/proc/net/unix",
        encoding=sys.getfilesystemencoding(),
        errors=sys.getfilesystemencodeerrors(),
    ) as file:
        return [line.split()[-2] for line in file if socket_path in line]


class _RpcServerProtocol(UnixSocketAuthProtocol):
    def __init__(self, loop, sink, user):
        self._loop = loop
        self._sink = sink
        self.user = user
        self._transport = None
        self._buf = LineBuffer()

    def preprocess_data(self, data: str):
        decoded = json.loads(data)
        user_type, user_name = hosting_panel.HostingPanel().authenticate(
            self, decoded
        )
        self.user = user_type
        if user_name is not None:
            decoded["params"]["user"] = user_name

        # add calling process
        try:
            calling_process = Process(self._pid).cmdline()
        except Exception as e:
            calling_process = [str(e)]
        decoded["calling_process"] = calling_process
        return decoded

    def data_received(self, data):
        self._buf.append(data.decode())
        for msg in self._buf:
            try:
                result = self.preprocess_data(msg)
                method = result["command"]
                params = result["params"]
                logger.debug("Data received: %r", data)

                cb = _apply_middleware(method, self.user)

                # TODO: fix that there is no json flag in params
                self._loop.create_task(
                    self._dispatch(
                        method, params, cb(result, self._sink, self.user)
                    )
                )

            except InvalidTokenException as e:
                # without events in Sentry
                logger.warning("Incorrect token provided")

                self._write_response({"result": ERROR, "messages": str(e)})

            except Exception as e:
                logger.exception(
                    "Something went wrong before processing %s", data.decode()
                )

                self._write_response({"result": ERROR, "messages": str(e)})

    async def _dispatch(self, method, params, coro):
        with inactivity.track.task("rpc_{}".format(method)):
            # route and save result to 'result'
            response = await _execute_request(coro, method)
            logger.info(
                "Response: method - {}, data - {}".format(method, response)
            )
            self._write_response(response)

    def connection_lost(self, transport):
        self._transport = None

    def _write_response(self, data):
        if self._transport is None:
            logger.warning("Cannot send RPC response: connection lost.")
            return
        else:
            try:
                self._transport.write((json.dumps(data) + "\n").encode())
            except Exception as e:
                logger.exception(e)  # TODO: need to own message error


def _check_socket_folder_permissions(socket_path):
    dir_name = os.path.dirname(socket_path)
    os.makedirs(dir_name, exist_ok=True)
    os.chmod(dir_name, 0o755)


class RpcServer:
    SOCKET_PATH = Config.SOCKET_PATH
    USER = UserType.ROOT
    SOCKET_MODE = 0o700

    @classmethod
    async def create(cls, loop, sink):
        _check_socket_folder_permissions(cls.SOCKET_PATH)
        if os.path.exists(cls.SOCKET_PATH):
            os.unlink(cls.SOCKET_PATH)
        server = await loop.create_unix_server(
            lambda: _RpcServerProtocol(loop, sink, cls.USER), cls.SOCKET_PATH
        )
        os.chmod(cls.SOCKET_PATH, cls.SOCKET_MODE)
        return server


class RpcServerAV:
    USER = UserType.ROOT
    SOCKET_PATH = Config.SOCKET_PATH
    PROTOCOL_CLASS = _RpcServerProtocol

    @classmethod
    async def create(cls, loop, sink):
        """Looking for socket in /proc/net/unix and check which descriptor
            corresponded to it by comparing inode

            $ ls -l /proc/[pid]/fd
            lrwx------ 1 root root 64 Apr 11 07:20 4 -> socket:[2866765]
            $ cat /proc/net/unix
            Num       RefCount Protocol Flags    Type St Inode Path
        ffff880054c0a4c0: 00000002 00000000 00010000 0001 01 2866765 /var/run/defence360agent/simple_rpc.sock # noqa
        """

        def safe_readlink(*args, **kwargs):
            """Return empty path on error."""
            with suppress(OSError):
                return os.readlink(*args, **kwargs)
            return ""

        # find inodes for the SOCKET_PATH
        _socket_path = cls.SOCKET_PATH
        _check_socket_folder_permissions(_socket_path)
        if _socket_path.startswith("/var/run"):
            # remove /var prefix, see DEF-16201
            _socket_path = _socket_path[len("/var") :]
        inodes = _find_uds_inodes(_socket_path)

        # find socket fds corresponding to the inodes
        last_error = None
        for inode in inodes:
            try:
                with os.scandir("/proc/self/fd") as it:
                    for fd in it:
                        if safe_readlink(fd.path) == "socket:[{}]".format(
                            inode
                        ):
                            socket_fd = int(fd.name)
                            break  # found fd
                    else:  # no break, not found fd for given inode
                        continue  # try another inode
                    break  # found fd
            except OSError as e:
                last_error = e
        else:  # no break, not found
            raise SocketError(
                "[{}] Socket {!r} for {} not found.".format(
                    "inode" * (not inodes), cls.SOCKET_PATH, cls.USER
                )
            ) from last_error

        _socket = socket.fromfd(
            socket_fd,
            socket.AF_UNIX,
            socket.SOCK_STREAM | socket.SOCK_NONBLOCK,
        )
        server = await loop.create_unix_server(
            lambda: cls.PROTOCOL_CLASS(loop, sink, cls.USER), sock=_socket
        )
        return server


class NonRootRpcServerAV(RpcServerAV):
    USER = UserType.NON_ROOT
    SOCKET_PATH = Config.NON_ROOT_SOCKET_PATH


class NonRootRpcServer(RpcServer):
    SOCKET_PATH = Config.NON_ROOT_SOCKET_PATH
    USER = UserType.NON_ROOT
    SOCKET_MODE = 0o777


class _RpcClientImpl:
    def __init__(self, socket_path):
        try:
            self._sock = socket.socket(
                socket.AF_UNIX, socket.SOCK_STREAM | socket.SOCK_NONBLOCK
            )
            self._sock.connect(socket_path)
        except (ConnectionRefusedError, FileNotFoundError, BlockingIOError):
            raise ServiceStateError()

    def dispatch(self, method, params):
        self._sock.sendall(
            (json.dumps({"command": method, "params": params}) + "\n").encode()
        )
        try:
            data = self._sock_recv_until(terminator_byte=b"\n")
        except ConnectionResetError as e:
            raise ResponseError(f"Connection reset: {e}") from e

        try:
            response = json.loads(data.decode())
        except Exception as e:
            raise ResponseError(
                "Error parsing RPC response {!r}".format(data)
            ) from e

        return response

    def _sock_recv_until(self, terminator_byte):
        assert not self._sock.getblocking()

        chunks = []
        while (not chunks) or (terminator_byte not in chunks[-1]):
            fdread_list = [self._sock.fileno()]
            rwx_fdlist = select.select(
                fdread_list,
                [],
                [],
                # naive timeout for one-shot response
                # scenario
                Config.CLIENT_TIMEOUT,
            )
            fdready_list = rwx_fdlist[0]

            if self._sock.fileno() not in fdready_list:
                if any(rwx_fdlist):
                    raise SocketError(
                        "select() = {!r} resulted in error".format(rwx_fdlist)
                    )
                else:
                    raise SocketError("request timeout")

            chunk = self._sock.recv(io.DEFAULT_BUFFER_SIZE)
            if len(chunk) == 0:
                raise SocketError("Empty response from socket.recv()")
            chunks.append(chunk)

        return b"".join(chunks)


class _NoRpcImpl:
    def __init__(self, sink=None):
        self._sink = sink
        # suppress is for doing those things idempotent way

        # PSSST! simplification.run_in_executor() is main thread now! :-X
        # with suppress(tls_check.OverridingReset):
        #     tls_check.reset("main CLI thread for stopped agent")

        with suppress(tls_check.OverridingReset):
            loop = asyncio.get_event_loop()
            loop.run_until_complete(run_in_executor(loop, tls_check.reset))

    def dispatch(self, method, params):
        loop = asyncio.get_event_loop()
        logger.info("Executing {}, params: {}".format(method, params))
        request = {"command": method, "params": params}

        cb = _apply_middleware(method, user=UserType.ROOT)
        return loop.run_until_complete(
            _execute_request(cb(request, self._sink), method)
        )


class RpcClient:
    """
    One RpcClient instance is suitable to use for multiple ipc calls

    :param RpcServiceState require_svc_is_running: whether to provide direct
        endpoints binding if the service is stopped.
    :param int reconnect_with_timeout: timeout in sec for reconnect retries
    :param int num_retries: number of reconnect retries

    """

    def __init__(
        self,
        *,
        require_svc_is_running=RpcServiceState.RUNNING,
        reconnect_with_timeout=None,
        num_retries=1,
    ):
        self._impl = None
        self._socket_path = (
            Config.SOCKET_PATH
            if is_root_user()
            else Config.NON_ROOT_SOCKET_PATH
        )

        if (
            require_svc_is_running == RpcServiceState.STOPPED
            and rpc_is_running()
        ):
            raise ServiceStateError(RpcServiceState.RUNNING)
        elif require_svc_is_running == RpcServiceState.RUNNING:
            # ensure that socket is active
            adaptor = svcctl.adaptor(Core.SVC_NAME)
            run_coro(adaptor.activate_socket_service())

        if require_svc_is_running in (
            RpcServiceState.ANY,
            RpcServiceState.RUNNING,
        ):
            try:
                if reconnect_with_timeout:
                    self._impl = self._reconnect_with_timeout(
                        reconnect_with_timeout, num_retries
                    )
                else:
                    self._impl = _RpcClientImpl(self._socket_path)
                return
            except ServiceStateError:
                if require_svc_is_running == RpcServiceState.RUNNING:
                    raise

        if self._impl is None:
            # In other cases (ANY, STOPPED, DIRECT) need to use _NoRpcImpl
            assert (
                is_root_user()
            ), "_NoRpcImpl is not available for non root user"
            self._impl = _NoRpcImpl()

    def __getattr__(self, method):
        return functools.partial(self._dispatch, method)

    def cmd(self, *command):
        return functools.partial(self._dispatch, command)

    def _dispatch(self, method, **params):
        response = self._impl.dispatch(method, params)

        if isinstance(method, (list, tuple)):
            if response["result"] in (ERROR, WARNING):
                return response["result"], response["messages"]
            else:
                assert response["result"] == SUCCESS
                return response["result"], response["data"]
        else:
            if response["result"] in (ERROR, WARNING):
                raise ResponseError(response["messages"])

            return response["data"]

    def _reconnect_with_timeout(self, timeout, num_retries):
        while True:
            try:
                return _RpcClientImpl(self._socket_path)
            except ServiceStateError:
                if num_retries:
                    logger.info(
                        "Waiting %d second(s) before retry...", timeout
                    )
                    time.sleep(timeout)
                    num_retries -= 1
                else:
                    raise