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:/proc/thread-self/root/opt/cloudlinux/venv/lib/python3.11/site-packages/guppy/heapy/
Upload File :
Current File : //proc/thread-self/root/opt/cloudlinux/venv/lib/python3.11/site-packages/guppy/heapy/Monitor.py
import os
import pprint
import signal
import socket
import socketserver
import sys
import threading
import time
import traceback
import pickle as pickle

try:
    import readline  # Imported to _enable_ command line editing
except ImportError:
    pass

import select
import queue

from guppy.heapy.RemoteConstants import *
from guppy.heapy.Console import Console
from guppy.sets import mutnodeset
from guppy.etc.etc import ptable
from guppy.etc import cmd


class Server(socketserver.ThreadingTCPServer):
    pass


def ioready(fd, wait):
    r, w, x = select.select([fd], [], [], wait)
    return len(r)


def queue_get_interruptible(q, noblock=0):
    while 1:
        try:
            return q.get(timeout=0.2)
        except queue.Empty:
            if noblock:
                break

# Special value signals that connection has been closed


CONN_CLOSED = ('CLOSED',)


class Handler(socketserver.StreamRequestHandler):
    allow_reuse_address = 1

    def close(self):
        if not self.isclosed.tas(0):
            self.server.monitor.remove_connection(self)
            self.dataq.put(CONN_CLOSED)
            self.request.shutdown(2)
            self.request.close()

    def send_cmd(self, cmd):
        if not cmd.endswith('\n'):
            cmd += '\n'
        self.request.send(cmd.encode('utf-8'))

    def browser_cmd(self, cmd):
        if self.prompt == '>>> ':
            self.exec_cmd('q', retdata=1)
        if self.prompt == '<Annex> ':
            self.exec_cmd('cont', retdata=1)
        return self.exec_cmd(cmd, retdata=1)

    def exec_cmd(self, cmd, retdata=0, noblock=0):
        if cmd is not None:
            self.send_cmd(cmd)
            self.promptstate = False
        datas = []
        while 1:
            p = queue_get_interruptible(self.dataq, noblock)
            if p is None:
                if self.promptstate:
                    break
                else:
                    time.sleep(1)
                    continue
            if p is CONN_CLOSED:
                raise EOFError
            if p[0] == 'DATA':
                self.promptstate = False
                if retdata:
                    datas.append(p[1])
                else:
                    sys.stdout.write(p[1])
            elif p[0] == 'PROMPT':
                self.prompt = p[1]
                if self.dataq.empty():
                    self.promptstate = True
                    break
                else:
                    self.promptstate = False
            else:
                assert 0
        if retdata:
            return ''.join(datas)

    def get_ps(self, name):
        for line in self.firstdata.split('\n'):
            if line.startswith(name):
                if '=' in line:
                    ix = line.index('=')
                    line = line[ix+1:].strip()
                return line
        return ''

    def get_val(self, expr):
        data = self.browser_cmd('dump %s' % expr)
        return pickle.loads(data)

    def handle(self):
        self.prompt = None
        self.promptstate = False
        self.isclosed = mutnodeset()
        self.dataq = queue.Queue()

        self.server.monitor.add_connection(self)

        while 1:
            try:
                data = self.rfile.readline().decode('utf-8')
                if not data:
                    raise EOFError('End of file')
                if data.endswith(DONE):
                    raise EOFError('DONE')
            except (EOFError, socket.error):
                break
            if data.endswith(READLINE):
                prompt = data[:-len(READLINE)]
                self.dataq.put(('PROMPT', prompt))
                if self.prompt is None:
                    self.firstdata = self.exec_cmd(cmd=None, retdata=1)
            else:
                self.dataq.put(('DATA', data))
        self.close()


class MonitorConnection(cmd.Cmd):
    use_raw_input = 1

    def __init__(self, monitor):
        self.aliases = {}
        cmd.Cmd.__init__(self)
        self.hno = 0
        self.isclosed = 0
        self.forceexit = 0
        self.prompt = '<Monitor> '

        self.monitor = monitor
        self.server = s = Server((LOCALHOST, HEAPYPORT), Handler)
        self.server.monitor = monitor
        self.st = threading.Thread(target=self.run_server,
                                   args=())
        self.st.start()

    def close(self):
        self.isclosed = 1
        self.server.socket.shutdown(2)
        self.server.server_close()
        self.server.verify_request = lambda x, y: 0

    def default(self, line):
        cmd.Cmd.default(self, line)
        cmd.Cmd.do_help(self, '')

    def run_server(self):
        s = self.server
        while not self.isclosed:
            s.handle_request()
        s.server_close()

    def exec_cmd(self, cmd):
        if not cmd:
            # I don't want the repeat of previous command when giving
            # empty command that is provided by cmd.py.
            # It turned out to be confusing sometimes.
            return
        line = cmd
        try:
            line = self.precmd(line)
            stop = self.onecmd(line)
            stop = self.postcmd(stop, line)
            return stop
        except BaseException:
            self.handle_error(line)

    def handle_error(self, cmdline):
        """Handle an error gracefully.  May be overridden.

        The default is to print a traceback and continue.

        """
        print('-'*40, file=sys.stderr)
        print('Exception happened during processing the command',
              end=' ', file=sys.stderr)
        print(repr(cmdline), file=sys.stderr)
        import traceback
        traceback.print_exc()
        print('-'*40, file=sys.stderr)

    # Alias handling etc copied from pdb.py in Python dist

    def precmd(self, line):
        """Handle alias expansion and ';;' separator."""
        self.curline = line
        if not line:
            return line
        args = line.split()
        while args[0] in self.aliases:
            line = self.aliases[args[0]]
            if '%' in line:
                ii = 1
                for tmpArg in args[1:]:
                    line = line.replace("%" + str(ii),
                                        tmpArg)
                    line = line.replace('%>=' + str(ii),
                                        ' '.join(args[ii:]))
                    ii = ii + 1
                line = line.replace("%*", ' '.join(args[1:]))
            else:
                line = line + ' ' + ' '.join(args[1:])
            args = line.split()
        # split into ';;' separated commands
        # unless it's an alias command
        if args[0] != 'alias':
            marker = line.find(';;')
            if marker >= 0:
                # queue up everything after marker
                next = line[marker+2:].lstrip()
                self.cmdqueue.append(next)
                line = line[:marker].rstrip()
        return line

    def do_exit(self, arg):
        self.forceexit = 1
        return 1

    def help_exit(self):
        print("""exit
-----
Exit from the monitor and from the Python process that started it.
This makes sure to exit without waiting for the server thread to terminate.
See also the q command.""")

    do_h = cmd.Cmd.do_help

    def help_h(self):
        print("""h(elp)
-----
Without argument, print the list of available commands.
With a command name as argument, print help about that command.""")

    def help_help(self):
        self.help_h()

    def do_int(self, arg):
        try:
            con = Console(stdin=self.stdin, stdout=self.stdout,
                          locals=self.__dict__)
            con.interact(
                "Local interactive console. To return to Monitor, type %r." %
                con.EOF_key_sequence)

        finally:
            pass

    def help_int(self):
        print("""int
-----
Local interactive console.
This will bring up a Python console locally in
the same interpreter process that the Monitor itself.""")

    def do_ki(self, arg):
        if not arg:
            arg = self.conid
        arg = int(arg)
        c = self.monitor.connections[arg]
        if c.get_ps('noninterruptible'):
            print('''\
Error: Can not interrupt this remote connection (uses Python < 2.4)''')
        else:
            print('Sending KeyboardInterrupt to connection %s.' % arg)
            c.send_cmd(KEYBOARDINTERRUPT)

    def help_ki(self):
        print("""ki <connection ID>
-----
Keyboard Interrupt

Send a command to interrupt the remote thread on the specified
connection (default is the last one connected to).

Notes:

It currently only works with Python >= 2.4.  The remote thread will
not always be awakened, for example if it is waiting in
time.sleep(). Sometimes using several ki commands helps.""")

    def do_lc(self, arg):
        table = [['CID', 'PID', 'ARGV']]
        for cid, con in list(self.monitor.connections.items()):
            table.append([cid,
                          con.get_ps('target.pid'),
                          con.get_ps('target.sys.argv')])

        ptable(table, self.stdout)

    def help_lc(self):
        print("""lc
-----
List Connections.
List the currently open connections.
The table printed has one line per connection in this form:

CID PID   ARGV
  1 17999 ['/home/nilsson/bin/solitaire.py']

CID is the connection ID, which may be used as argument to the sc
command.

PID is the process ID of the target interpreter process.  In Linux,
this is the parent of the remote control interpreter thread that runs
the Annex that the connection is talking to.

ARGV is the argument vector in the target Python interpereter.""")

    def do_sc(self, arg):
        if arg:
            self.conid = int(arg)
        print('Remote connection %d. To return to Monitor, type <Ctrl-C> or .<RETURN>' % self.conid)
        self.monitor.set_connection(self.monitor.connections[self.conid])

    def help_sc(self):
        print("""sc <connection ID>
-----
Set connection to communicate with a remote thread.

With an argument, set current connection to the number specified.
Without argument, use the same connection as last time.  You will then
be talking to a remote process via that connection.  You can return to
Monitor at any time by <Ctrl-C>. You may also use the '.' command
(followed by <Return>), if the remote process is waiting for input.
The '.' character may be followed by a monitor command, to execute it
directly instead of returning to the monitor. For example, when
talking to a connection, '.sc 1' will directly change to connection 1.""")

    def do_q(self, arg):
        return 1

    def help_q(self):
        print("""q
-----
Quit from the monitor.
This will not exit from Python itself if called from an interactive
interpreter. To make sure to exit from Python, use the exit command.""")


class Monitor:
    use_raw_input = 1

    def __init__(self):
        self.connection = self.monitor_connection = MonitorConnection(self)
        self.connections = {}
        self.ids = 0
        self.prompt = None

    def newid(self):
        if not self.connections:
            self.ids = 1
            self.monitor_connection.conid = self.ids
        else:
            self.ids = max([1]+[c for c in list(self.connections.keys())])+1
        return self.ids

    def add_connection(self, connection):
        hid = self.newid()
        self.connections[hid] = connection
        connection.monitor_id = hid
        self.print_async('*** Connection %s opened ***' % hid)

    def print_async(self, text):
        """ Print text only if we are waiting for input,
        and then restore the prompt. """
        if self.prompt is not None:
            print('\n'+text)
            sys.stdout.write(self.prompt)
            sys.stdout.flush()

    def remove_connection(self, connection):
        del self.connections[connection.monitor_id]
        if connection is self.connection:
            self.set_connection(self.monitor_connection)
        self.print_async('*** Connection %s closed ***' %
                         connection.monitor_id)

    def run(self):
        try:
            stop = 0
            while not stop:
                try:
                    while not stop:
                        conn = self.connection
                        self.prompt = conn.prompt
                        if conn is not self.monitor_connection:
                            conn.exec_cmd(cmd=None, noblock=1)
                        cmd = input(conn.prompt)
                        self.prompt = None
                        conn = None
                        if cmd.startswith('.'):
                            if cmd == '.':
                                self.connection = self.monitor_connection
                            else:
                                cmd = cmd[1:]
                                conn = self.monitor_connection
                        # elif cmd or self.connection is self.monitor_connection:
                        else:
                            conn = self.connection
                        if conn:
                            try:
                                r = conn.exec_cmd(cmd)
                            except EOFError:
                                r = 1
                            if conn is self.monitor_connection and r:
                                stop = 1
                except EOFError:
                    'We better exit in case the input is from a file'
                    print('*** End Of File - Exiting Monitor ***')
                    self.connection = self.monitor_connection
                    stop = 1
                except KeyboardInterrupt:
                    print('KeyboardInterrupt')
                    print('Use the ki command to interrupt a remote process.')
                    self.connection = self.monitor_connection
                    continue

        finally:
            self.prompt = None  # Avoid closing messages
            self.close()

    def close(self):
        for c in list(self.connections.values()):
            try:
                c.close()
            except socket.error:
                pass
        try:
            self.monitor_connection.close()
        except socket.error:
            pass
        if self.monitor_connection.forceexit:
            os._exit(0)

    def set_connection(self, connection):
        self.connection = connection
        self.prompt = connection.prompt


def monitor():
    """monitor() [0]

Start an interactive remote monitor.

This can be used to get information about the state, in
particular the memory usage, of separately running Python
processes.

References
    [0] heapy_Use.html#heapykinds.Use.monitor"""
    from guppy.heapy import Remote
    Remote.off()
    m = Monitor()
    m.run()


if __name__ == '__main__':
    monitor()