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/lib64/python3.11/site-packages/clselect/
Upload File :
Current File : //proc/thread-self/root/opt/cloudlinux/venv/lib64/python3.11/site-packages/clselect/clselect.py
# -*- coding: utf-8 -*-

# Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2019 All Rights Reserved
#
# Licensed under CLOUD LINUX LICENSE AGREEMENT
# http://cloudlinux.com/docs/LICENSE.TXT

from __future__ import absolute_import
from __future__ import print_function
from __future__ import division
import os
import re
import sys
from glob import glob

from builtins import map

from future.moves import configparser as ConfigParser

from .clselectexcept import ClSelectExcept, BaseClSelectException
from .clselectprint import clprint
from clselect.utils import in_cagefs


class ClSelect(object):
    BASE_ETC_CL_SELECTOR = '/etc/cl.selector.conf.d' if in_cagefs() else '/etc/cl.selector'
    CONFIG_PATH = f'{BASE_ETC_CL_SELECTOR}/selector.conf'
    DEFAULTS_PATH = f'{BASE_ETC_CL_SELECTOR}/defaults.cfg'
    DEFAULT_PHP_PATH = '/usr/bin/php'
    NATIVE_CONF = f'{BASE_ETC_CL_SELECTOR}/native.conf'
    USER_CONF = f'{BASE_ETC_CL_SELECTOR}/user.conf'
    CONFIGS_DIR = f'{BASE_ETC_CL_SELECTOR}/php.extensions.d/'
    _CAGFSCTL = "/usr/sbin/cagefsctl"

    # Cache PHP index file
    CACHEFILE_DIR = '/var/lve'
    CACHEFILE_CAGEFS_DIR = '/var/lve/php.dat.d'

    CACHEFILE_PHP_PATTERN = '/php%s.dat'
    CACHEFILE_PATTERN = CACHEFILE_DIR + CACHEFILE_PHP_PATTERN
    CACHEFILE_CAGEFS_PATTERN = CACHEFILE_CAGEFS_DIR + CACHEFILE_PHP_PATTERN

    CACHEFILE_PHP_NATIVE_PATTERN = '/php_native_ver.dat'
    CACHEFILE_NATIVE_VER_PATTERN = CACHEFILE_DIR + CACHEFILE_PHP_NATIVE_PATTERN
    CACHEFILE_NATIVE_VER_CAGEFS_PATTERN = CACHEFILE_CAGEFS_DIR + CACHEFILE_PHP_NATIVE_PATTERN

    @staticmethod
    def check_multiphp_system_default_version():
        if in_cagefs():
            return

        try:
            from clcagefslib.selector.configure import multiphp_system_default_is_ea_php, selector_modules_must_be_used
        except ImportError:
            raise BaseClSelectException('CageFS not installed.')

        if not multiphp_system_default_is_ea_php() and not selector_modules_must_be_used():
            raise BaseClSelectException('system default PHP version is alt-php. PHP Selector is disabled. Use cPanel MultiPHP manager instead.')

    @staticmethod
    def work_without_cagefs():
        return os.path.exists(ClSelect.USER_CONF)

    def __init__(self, item='php'):
        self._item = item
        self._dh = self._get_default_config_handler()
        self._selector_contents = {}
        self._native_contents = {}
        self._hidden_extensions = set()
        self._native_version = None
        self.without_cagefs = ClSelect.work_without_cagefs()
        self._load_config_files()

    def check_requirements(self):
        # check that selectorctl and cagefsctl are available
        utilities = [
            (
                lambda: os.path.exists(self._get_native_path('cli')),
                ClSelectExcept.NativeNotInstalled(self._get_native_path('cli'))
            ),
            (
                lambda: in_cagefs() or os.path.exists(self._CAGFSCTL),
                ClSelectExcept.MissingCagefsPackage()
            ),
        ]

        for predicate, error in utilities:
            if predicate():
                continue
            raise error

    def _load_config_files(self):
        for filename in glob(os.path.join(self.CONFIGS_DIR, '*.cfg')):
            self._load_config_file(filename)

    def _load_config_file(self, filepath):
        dh = ConfigParser.SafeConfigParser(interpolation=None, strict=False)

        try:
            dh.read(filepath)
        except ConfigParser.Error as e:
            raise ClSelectExcept.FileProcessError(
                filepath, message="Config is malformed, error: %s" % str(e))

        try:
            self._hidden_extensions.update(
                dh.get('extensions', 'hide_extensions').split(','))
        except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
            pass

    def list_alternatives(self):
        """
        Returns alternatives summary as tuple
        :rtype: tuple
        """
        alternatives = self.get_all_alternatives_data()
        list_of_alternatives = []

        for alt in sorted(alternatives.keys()):
            try:
                list_of_alternatives.append(
                    (alt,
                     alternatives[alt]['version'],
                     alternatives[alt]['data'][self._item]))
            except KeyError:
                continue
        return tuple(list_of_alternatives)

    def get_all_alternatives_data(self):
        """
        Returns dict of all selector config contents.
        If no data loads them
        :return:
        {'4.4': {'version': '4.4.9', 'data': {'lsphp': '/opt/alt/php44/usr/bin/lsphp',
                                              'php.ini': '/opt/alt/php44/etc/php.ini',
                                              'php': '/opt/alt/php44/usr/bin/php-cgi',
                                              'php-cli': '/opt/alt/php44/usr/bin/php'}}}
        :rtype: dict
        """
        if not self._selector_contents:
            try:
                self._load_alternatives_config()
            except (ClSelectExcept.ConfigNotFound, ClSelectExcept.WrongConfigFormat):
                return {}
        return self._selector_contents

    def get_alternatives_data(self, version):
        """
        Returns selector config contents of certain version as dict.
        If no data loads them
        @param version: string, selector version
        @return: dict
        """
        if not self._selector_contents:
            self._load_alternatives_config()
        try:
            return {version: self._selector_contents[version]}
        except KeyError:
            raise ClSelectExcept.NoSuchAlternativeVersion(version)

    def get_version(self, show_native_version=False):
        """
        Gets default selector version
        """
        alternatives = self.get_all_alternatives_data()
        try:
            version = self._dh.get('versions', self._item)
            return (
                version,
                alternatives[version]['version'],
                alternatives[version]['data'][self._item])
        except (ConfigParser.NoSectionError, KeyError):
            return self._compose_native_info(show_native_version)

    def set_version(self, version):
        """
        Sets default selector version
        """
        alternatives = self.get_all_alternatives_data()
        self._check_alternative(version, alternatives)
        defaults_contents = self._process_ini_file(
            self.DEFAULTS_PATH,
            ('versions',),
            self._add_or_change_option,
            (self._item, version))
        self._write_to_file(
            '\n'.join(defaults_contents), self.DEFAULTS_PATH)

    def enable_version(self, version):
        """
        Removes disabled state from version
        """
        alternatives = self.get_all_alternatives_data()
        self._check_alternative(version, alternatives)
        defaults_contents = self._process_ini_file(
            self.DEFAULTS_PATH,
            (self._item, version),
            self._remove_option,
            'state')
        self._write_to_file(
            '\n'.join(defaults_contents), self.DEFAULTS_PATH)

    def disable_version(self, version):
        """
        Marks a vesrion as disabled
        """
        alternatives = self.get_all_alternatives_data()
        self._check_alternative(version, alternatives)
        defaults_contents = self._process_ini_file(
            self.DEFAULTS_PATH,
            (self._item, version),
            self._add_or_change_option,
            ('state', 'disabled'))
        self._write_to_file(
            '\n'.join(defaults_contents), self.DEFAULTS_PATH)

    def is_version_enabled(self, version):
        """
        Method that allows you to check if some version is enabled in config.
        E.g. is_version_enabled('5.4') -> True
        :rtype: bool
        """
        return not self._dh.has_option(
            "%s%s" % (self._item, version), 'state')

    def get_summary(self, show_native_version=False):
        """
        Returns state of alternatives
        @return: tuple[version, tuple[isEnabled, isDefault]]
        """
        alternatives = self.get_all_alternatives_data()
        native_info = self._compose_native_info(show_native_version)
        summary = {'native': {'enabled': True, 'default': False}}
        alt_versions = sorted(alternatives.keys()) + ['native']
        for version in alt_versions:
            if version not in summary:
                summary[version] = {}
            summary[version]['enabled'] = self.is_version_enabled(version)
            summary[version]['default'] = False
        try:
            default_version = self._dh.get('versions', self._item)
        except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
            default_version = 'native'
        try:
            summary[default_version]['default'] = True
        except KeyError:
            raise ClSelectExcept.NoSuchAlternativeVersion(default_version)
        summary[native_info[0]] = summary.pop('native')
        alt_versions.remove('native')
        alt_versions.append(native_info[0])
        for idx in range(len(alt_versions)):
            v = alt_versions[idx]
            alt_versions[idx] = (
                v,
                (summary[v]['enabled'],
                 summary[v]['default']))
        return tuple(alt_versions)

    def _load_alternatives_config(self):
        """
        Parses selector config file and fills an instance config dict. Example:
        {'4.4': {'version': '4.4.9', 'data': {'lsphp': '/opt/alt/php44/usr/bin/lsphp',
                                              'php.ini': '/opt/alt/php44/etc/php.ini',
                                              'php': '/opt/alt/php44/usr/bin/php-cgi',
                                              'php-cli': '/opt/alt/php44/usr/bin/php'}}}
        :raises ClSelectExcept.ConfigNotFound:
        :raises ClSelectExcept.WrongConfigFormat:
        """
        try:
            f = open(self.CONFIG_PATH)
            for line in f:
                stripped_line = line.strip()
                if stripped_line == "":
                    continue
                (item, short_version, long_version,
                    item_path) = stripped_line.split()
                if self._item not in item:
                    continue
                if short_version not in self._selector_contents:
                    self._selector_contents[short_version] = {}
                self._selector_contents[short_version]['version'] = long_version
                if 'data' not in self._selector_contents[short_version]:
                    self._selector_contents[short_version]['data'] = {}
                self._selector_contents[short_version]['data'][item] = item_path
            if not self._selector_contents:
                raise ClSelectExcept.ConfigNotFound(None,
                    message = 'alt-php packages not found')
        except (OSError, IOError) as e:
            raise ClSelectExcept.ConfigNotFound(
                'Cannot read %s: %s. Native used' % (self.CONFIG_PATH, e),
                message = 'alt-php packages not found')
        except ValueError:
            raise ClSelectExcept.WrongConfigFormat(self.CONFIG_PATH)

    def _get_default_config_handler(self, path=None):
        """
        Gets ConfigParser handler for future use
        """
        dh = ConfigParser.ConfigParser(interpolation=None, strict=False)
        dh.optionxform = str
        if path:
            dh.read(path)
        else:
            dh.read(self.DEFAULTS_PATH)
        return dh

    def _check_alternative(version, alternatives):
        if version != 'native' and version not in alternatives:
            raise ClSelectExcept.NoSuchAlternativeVersion(version)
    _check_alternative = staticmethod(_check_alternative)

    def _make_section_header(section_info):
        """
        Gets section header data tuple and returns ini section header string
        @param section_info: tuple
        @return: string
        """
        section_fmt = "[%s]" % ''.join(['%s'] * len(section_info))
        return section_fmt % section_info
    _make_section_header = staticmethod(_make_section_header)

    def _smooth_data(data):
        """
        Removes empty lines from list and appends newline if missing
        """
        data = list(filter((lambda i: i != ''), data))
        if not data or data[-1] != '\n':
            data.append('\n')
        return data
    _smooth_data = staticmethod(_smooth_data)

    def _process_ini_file(self, path, section_info, function, data, trace=True, action = None):
        """
        Parses ini file by sections,
        calls supplied callable to modify section is question,
        returns file as list of strings
        """
        contents = []
        no_section_contents = []
        section = []
        in_section = False
        found = False
        has_default = False
        section_header = self._make_section_header(section_info)
        try:
            f = open(path)
            for line in f:
                line = line.strip()
                if line.startswith('['):
                    in_section = True
                    if '[versions]' in line:
                        has_default = True
                    if section_header == line:
                        found = True
                    if len(no_section_contents) != 0:
                        contents.extend(no_section_contents)
                        no_section_contents = []
                    contents.extend(function(section_info, section, data, trace))
                    section = [line]
                    continue
                if in_section:
                    section.append(line)
                else:
                    no_section_contents.append(line)
            contents.extend(function(section_info, section, data, trace))
            f.close()
        except (OSError, IOError):
            pass
        if not has_default and '[versions]' not in section_header:
            default = ['[versions]', '%s = native' % self._item, '']
            default.extend(contents)
            contents = default
        if not found:
            try:
                build_in = self._get_builtins('native')
            except ClSelectExcept.UnableToGetExtensions:
                pass  # skip if native version not installed
            if action == 'disable_extentions':
                contents.extend(function(section_info, [section_header, 'modules = ' + ','.join(build_in)], data, trace))
            elif action == 'enable_extentions':
                data.extend(build_in)
                contents.extend(function(section_info, [section_header], data, trace))
            else:
                contents.extend(function(section_info, [section_header], data, trace))
        return contents

    def _get_php_binary_path(self, version):
        """
        Retrives path to php binary for supplied version
        :param version: php version to retrive path
        :return: path to php binary. If alternative version not found native php binary path returned
        """
        item = "%s-cli" % self._item
        alternatives = self.get_all_alternatives_data()
        try:
            path = alternatives[version]['data'][item]
        except KeyError:
            path = self._get_native_path(suffix='cli')
        return path

    def get_all_php_binaries_paths(self):
        """
        Retrives paths to php binary for all versions
        :return: Dictionary version -> path. Example:
            { '5.2': '/opt/alt/php52/usr/bin/php',
              '5.3': '/opt/alt/php53/usr/bin/php',
              'native': '/usr/bin/php'
            }
        """
        alternatives = self.get_all_alternatives_data()
        paths_dict = {'native': self._get_php_binary_path('native')}
        for version in alternatives.keys():
            paths_dict[version] = self._get_php_binary_path(version)
        return paths_dict

    def _read_php_cache_file(self, version):
        """
        Retrives contents of cache file for supplied php version
        :param version: PHP version to read file
        :return: file contents
        """
        filename = self.CACHEFILE_CAGEFS_PATTERN % version if in_cagefs() else self.CACHEFILE_PATTERN % version
        with open(filename, "r") as f:
            return f.read()

    def _get_builtins(self, version):
        """
        Gets php extensions from the /var/lve/phpX.X.dat cache file,
        which contains list of modules that are either compiled-in
        or enabled in /opt/alt/phpXX/etc/php.ini config file
        """
        builtins = []
        # Read php output from cache file for approptiate php version
        try:
            output = self._read_php_cache_file(version)
        except (OSError, IOError):
            raise ClSelectExcept.UnableToGetExtensions(version)

        # Section that contains list of modules usually starts and ends with
        # [PHP Modules] and [Zend Modules] headers respectively
        start_pattern, end_pattern = "[PHP Modules]", "[Zend"
        start_index, end_index = output.find(start_pattern), output.find(end_pattern)
        start_index = 0 if start_index == -1 else start_index + len(start_pattern)
        modules_list = output[start_index:end_index]

        module_pattern = re.compile(r"\w")
        for ext in modules_list.split("\n"):
            if not module_pattern.match(ext):
                continue
            module = "_".join(re.split("\s+", ext.lower()))
            if module not in self._hidden_extensions:
                builtins.append(module)
        return builtins

    def _remove_option(self, section_info, section, data, trace=True):
        """
        Adds 'modules' option to section or extends it
        @param section_info: tuple (item and version)
        @param section: list
        @param data: string
        @return: list
        """
        section_header = self._make_section_header(section_info)
        if len(section) == 0 or section_header != section[0]:
            return section
        return self._smooth_data(
            list(filter((lambda i: not i.startswith(data)), section)))

    def _add_or_change_option(self, section_info, section, data, trace=True):
        """
        Adds 'modules' option to section or extends it
        @param section_info: tuple
        @param section: list
        @param data: tuple
        @return: list
        """
        section_header = self._make_section_header(section_info)
        if len(section) == 0 or section_header != section[0]:
            return section
        oidx = None
        for idx in range(len(section)):
            if section[idx].startswith(data[0]):
                oidx = idx
                break
        option = "%s = %s" % data
        if oidx:
            section[oidx] = option
        else:
            section.append(option)
        return self._smooth_data(section)

    def _write_to_file(self, file_contents, file_path):
        """
        Saves data to file
        """
        try:
            f = open(file_path, 'w')
            f.write("%s\n" % file_contents)
            f.close()
        except (OSError, IOError) as e:
            raise ClSelectExcept.UnableToSaveData(file_path, e)

    def _get_native_path(self, suffix=None):
        """
        Returns path for native interpreter
        """
        suffixes = {
            'cli': '-cli',
            'ini': '.ini',
            'fpm': '-fpm'}
        item = self._item
        if suffix and suffix in suffixes:
            item = "%s%s" % (self._item, suffixes[suffix])
        if not self._native_contents:
            self._load_native_contents(self._item)
        if item in self._native_contents:
            path = self._native_contents[item]
            if os.path.isfile(path) and not os.path.islink(path):
                return path
        return self._native_contents[self._item]

    def _load_native_contents(self, value):
        """
        Parses native.conf file and loads it as dict, for example:
        {'php-fpm': '/usr/local/sbin/php-fpm', 'php.ini': '/usr/local/lib/php.ini', 'php': '/usr/bin/php', 'php-cli': '/usr/bin/php'}
        """
        try:
            f = open(self.NATIVE_CONF)
            for line in f:
                if line.startswith('#'):
                    continue
                if value not in line:
                    continue
                if '=' not in line:
                    continue
                item, path = list(map((lambda x: x.strip()), line.split('=')))
                self._native_contents[item] = path
            if value not in self._native_contents:
                self._native_contents[value] = self.DEFAULT_PHP_PATH
            f.close()
        except (OSError, IOError):
            self._native_contents[value] = self.DEFAULT_PHP_PATH

    def _compose_native_info(self, show_version=False):
        if not show_version:
            return 'native', 'native', self._get_native_path()
        native_version = self.get_native_version(verbose=False)
        if native_version:
            return ('native (%s)' % (native_version[0],),
                    'native (%s)' % (native_version[1],),
                    self._get_native_path())
        return 'native', 'native', self._get_native_path()

    def get_native_version(self, verbose=True):
        if self._native_version:
            return self._native_version
        version_pattern = re.compile(r'PHP\s+(?P<full>(?P<short>\d+\.\d+)\.\d+)')
        # Read php output from cache file for approptiate php version
        try:
            f = open(self.CACHEFILE_NATIVE_VER_PATTERN, "r")
            data = f.read()
            f.close()
        except (OSError, IOError) as e:
            if verbose:
                clprint.print_diag('text', {'status': 'ERROR', 'message': str(e)})
            return None
        for line in data.splitlines():
            m = version_pattern.match(line)
            if m:
                short, full = m.group('short'), m.group('full')
                self._native_version = (short, full)
                return short, full
        return None