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 : 45.32.152.128  /  Your IP : 216.73.216.91
Web Server : nginx/1.24.0
System : Linux stage-vultr 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64
User : forge ( 1000)
PHP Version : 8.2.14
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : ON  |  Pkexec : ON
Directory :  /usr/libexec/netdata/plugins.d/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /usr/libexec/netdata/plugins.d/charts.d.plugin
#!/usr/bin/env bash
# SPDX-License-Identifier: GPL-3.0-or-later
#
# charts.d.plugin allows easy development of BASH plugins
#
# if you need to run parallel charts.d processes, link this file to a different name
# in the same directory, with a .plugin suffix and netdata will start both of them,
# each will have a different config file and modules configuration directory.
#

export PATH="${PATH}:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/usr/sbin"

PROGRAM_FILE="$0"
MODULE_NAME="main"

# -----------------------------------------------------------------------------
# logging

PROGRAM_NAME="$(basename "${0}")"
SHORT_PROGRAM_NAME="${PROGRAM_NAME/.plugin/}"

# these should be the same with syslog() priorities
NDLP_EMERG=0   # system is unusable
NDLP_ALERT=1   # action must be taken immediately
NDLP_CRIT=2    # critical conditions
NDLP_ERR=3     # error conditions
NDLP_WARN=4    # warning conditions
NDLP_NOTICE=5  # normal but significant condition
NDLP_INFO=6    # informational
NDLP_DEBUG=7   # debug-level messages

# the max (numerically) log level we will log
LOG_LEVEL=$NDLP_INFO

set_log_min_priority() {
  case "${NETDATA_LOG_LEVEL,,}" in
    "emerg" | "emergency")
      LOG_LEVEL=$NDLP_EMERG
      ;;

    "alert")
      LOG_LEVEL=$NDLP_ALERT
      ;;

    "crit" | "critical")
      LOG_LEVEL=$NDLP_CRIT
      ;;

    "err" | "error")
      LOG_LEVEL=$NDLP_ERR
      ;;

    "warn" | "warning")
      LOG_LEVEL=$NDLP_WARN
      ;;

    "notice")
      LOG_LEVEL=$NDLP_NOTICE
      ;;

    "info")
      LOG_LEVEL=$NDLP_INFO
      ;;

    "debug")
      LOG_LEVEL=$NDLP_DEBUG
      ;;
  esac
}

set_log_min_priority

log() {
  local level="${1}"
  shift 1

  [[ -n "$level" && -n "$LOG_LEVEL" && "$level" -gt "$LOG_LEVEL" ]] && return

  systemd-cat-native --log-as-netdata <<EOFLOG
INVOCATION_ID=${NETDATA_INVOCATION_ID}
SYSLOG_IDENTIFIER=${PROGRAM_NAME}
PRIORITY=${level}
THREAD_TAG=charts.d.plugin
ND_LOG_SOURCE=collector
MESSAGE=${MODULE_NAME}: ${*//$'\n'/\\n}

EOFLOG
  # AN EMPTY LINE IS NEEDED ABOVE
}

info() {
  log "$NDLP_INFO" "${@}"
}

warning() {
  log "$NDLP_WARN" "${@}"
}

error() {
  log "$NDLP_ERR" "${@}"
}

fatal() {
  log "$NDLP_ALERT" "${@}"
	echo "DISABLE"
  exit 1
}

debug() {
  [ "$debug" = "1" ] && log "$NDLP_DEBUG" "${@}"
}

# -----------------------------------------------------------------------------
# check for BASH v4+ (required for associative arrays)

if [ ${BASH_VERSINFO[0]} -lt 4 ]; then
  echo >&2 "BASH version 4 or later is required (this is ${BASH_VERSION})."
  exit 1
fi

# -----------------------------------------------------------------------------
# create temp dir

debug=0
TMP_DIR=
chartsd_cleanup() {
	trap '' EXIT QUIT HUP INT TERM

	if [ ! -z "$TMP_DIR" -a -d "$TMP_DIR" ]; then
		[ $debug -eq 1 ] && echo >&2 "$PROGRAM_NAME: cleaning up temporary directory $TMP_DIR ..."
		rm -rf "$TMP_DIR"
	fi
	echo "EXIT" 2>/dev/null
	exit 0
}
trap chartsd_cleanup EXIT QUIT HUP INT TERM

if [ $UID = "0" ]; then
	TMP_DIR="$(mktemp -d /var/run/netdata-${PROGRAM_NAME}-XXXXXXXXXX)"
else
	TMP_DIR="$(mktemp -d /tmp/.netdata-${PROGRAM_NAME}-XXXXXXXXXX)"
fi

logdate() {
	date "+%Y-%m-%d %H:%M:%S"
}

# -----------------------------------------------------------------------------
# check a few commands

require_cmd() {
	local x=$(which "${1}" 2>/dev/null || command -v "${1}" 2>/dev/null)
	if [ -z "${x}" -o ! -x "${x}" ]; then
		warning "command '${1}' is not found in ${PATH}."
		eval "${1^^}_CMD=\"\""
		return 1
	fi

	eval "${1^^}_CMD=\"${x}\""
	return 0
}

require_cmd date || exit 1
require_cmd sed || exit 1
require_cmd basename || exit 1
require_cmd dirname || exit 1
require_cmd cat || exit 1
require_cmd grep || exit 1
require_cmd egrep || exit 1
require_cmd mktemp || exit 1
require_cmd awk || exit 1
require_cmd timeout || exit 1
require_cmd curl || exit 1

# -----------------------------------------------------------------------------

[ $((BASH_VERSINFO[0])) -lt 4 ] && fatal "BASH version 4 or later is required, but found version: ${BASH_VERSION}. Please upgrade."

info "started from '$PROGRAM_FILE' with options: $*"

# -----------------------------------------------------------------------------
# internal defaults
# netdata exposes a few environment variables for us

[ -z "${NETDATA_PLUGINS_DIR}" ] && NETDATA_PLUGINS_DIR="$(dirname "${0}")"
[ -z "${NETDATA_USER_CONFIG_DIR}" ] && NETDATA_USER_CONFIG_DIR="/etc/netdata"
[ -z "${NETDATA_STOCK_CONFIG_DIR}" ] && NETDATA_STOCK_CONFIG_DIR="/usr/lib/netdata/conf.d"

pluginsd="${NETDATA_PLUGINS_DIR}"
stockconfd="${NETDATA_STOCK_CONFIG_DIR}/${SHORT_PROGRAM_NAME}"
userconfd="${NETDATA_USER_CONFIG_DIR}/${SHORT_PROGRAM_NAME}"
olduserconfd="${NETDATA_USER_CONFIG_DIR}"
chartsd="$pluginsd/../charts.d"

minimum_update_frequency="${NETDATA_UPDATE_EVERY-1}"
update_every=${minimum_update_frequency} # this will be overwritten by the command line

# work around for non BASH shells
charts_create="_create"
charts_update="_update"
charts_check="_check"
charts_underscore="_"

# when making iterations, charts.d can loop more frequently
# to prevent plugins missing iterations.
# this is a percentage relative to update_every to align its
# iterations.
# The minimum is 10%, the maximum 100%.
# So, if update_every is 1 second and time_divisor is 50,
# charts.d will iterate every 500ms.
# Charts will be called to collect data only if the time
# passed since the last time the collected data is equal or
# above their update_every.
time_divisor=50

# number of seconds to run without restart
# after this time, charts.d.plugin will exit
# netdata will restart it
restart_timeout=$((3600 * 4))

# check if the charts.d plugins are using global variables
# they should not.
# It does not currently support BASH v4 arrays, so it is
# disabled
dryrunner=0

# check for timeout command
check_for_timeout=1

# the default enable/disable value for all charts
enable_all_charts="yes"

# -----------------------------------------------------------------------------
# parse parameters

check=0
chart_only=
while [ ! -z "$1" ]; do
	if [ "$1" = "check" ]; then
		check=1
		shift
		continue
	fi

	if [ "$1" = "debug" -o "$1" = "all" ]; then
		debug=1
		LOG_LEVEL=$NDLP_DEBUG
		shift
		continue
	fi

	if [ -f "$chartsd/$1.chart.sh" ]; then
		debug=1
		LOG_LEVEL=$NDLP_DEBUG
		chart_only="$(echo $1.chart.sh | sed "s/\.chart\.sh$//g")"
		shift
		continue
	fi

	if [ -f "$chartsd/$1" ]; then
		debug=1
		LOG_LEVEL=$NDLP_DEBUG
		chart_only="$(echo $1 | sed "s/\.chart\.sh$//g")"
		shift
		continue
	fi

	# number check
	n="$1"
	x=$((n))
	if [ "$x" = "$n" ]; then
		shift
		update_every=$x
		[ $update_every -lt $minimum_update_frequency ] && update_every=$minimum_update_frequency
		continue
	fi

	fatal "Cannot understand parameter $1. Aborting."
done

# -----------------------------------------------------------------------------
# loop control

# default sleep function
LOOPSLEEPMS_HIGHRES=0
now_ms=
current_time_ms_default() {
	now_ms="$(date +'%s')000"
}
current_time_ms="current_time_ms_default"
current_time_ms_accuracy=1
mysleep="sleep"

# if found and included, this file overwrites loopsleepms()
# and current_time_ms() with a high resolution timer function
# for precise looping.
source "$pluginsd/loopsleepms.sh.inc"
[ $? -ne 0 ] && error "Failed to load '$pluginsd/loopsleepms.sh.inc'."

# -----------------------------------------------------------------------------
# load my configuration

for myconfig in "${NETDATA_STOCK_CONFIG_DIR}/${SHORT_PROGRAM_NAME}.conf" "${NETDATA_USER_CONFIG_DIR}/${SHORT_PROGRAM_NAME}.conf"; do
	if [ -f "$myconfig" ]; then
		source "$myconfig"
		if [ $? -ne 0 ]; then
			error "Config file '$myconfig' loaded with errors."
		else
			info "Configuration file '$myconfig' loaded."
		fi
	else
		warning "Configuration file '$myconfig' not found."
	fi
done

# make sure time_divisor is right
time_divisor=$((time_divisor))
[ $time_divisor -lt 10 ] && time_divisor=10
[ $time_divisor -gt 100 ] && time_divisor=100

# we check for the timeout command, after we load our
# configuration, so that the user may overwrite the
# timeout command we use, providing a function that
# can emulate the timeout command we need:
# > timeout SECONDS command ...
if [ $check_for_timeout -eq 1 ]; then
	require_cmd timeout || exit 1
fi

# -----------------------------------------------------------------------------
# internal checks

# netdata passes the requested update frequency as the first argument
update_every=$((update_every + 1 - 1))     # makes sure it is a number
test $update_every -eq 0 && update_every=1 # if it is zero, make it 1

# check the charts.d directory
[ ! -d "$chartsd" ] && fatal "cannot find charts directory '$chartsd'"

# -----------------------------------------------------------------------------
# library functions

fixid() {
	echo "$*" |
		tr -c "[A-Z][a-z][0-9]" "_" |
		sed -e "s|^_\+||g" -e "s|_\+$||g" -e "s|_\+|_|g" |
		tr "[A-Z]" "[a-z]"
}

isvarset() {
  [ -n "$1" ] && [ "$1" != "unknown" ] && [ "$1" != "none" ]
  return $?
}

getosid() {
  if isvarset "${NETDATA_CONTAINER_OS_ID}"; then
    echo "${NETDATA_CONTAINER_OS_ID}"
  else
    echo "${NETDATA_SYSTEM_OS_ID}"
  fi
}

run() {
	local ret pid="${BASHPID}" t

	if [ "z${1}" = "z-t" -a "${2}" != "0" ]; then
		t="${2}"
		shift 2
		timeout "${t}" "${@}" 2>"${TMP_DIR}/run.${pid}"
		ret=$?
	else
		"${@}" 2>"${TMP_DIR}/run.${pid}"
		ret=$?
	fi

	if [ ${ret} -ne 0 ]; then
		{
			printf "$(logdate): ${PROGRAM_NAME}: ${status}: ${MODULE_NAME}: command '"
			printf "%q " "${@}"
			printf "' failed with code ${ret}:\n --- BEGIN TRACE ---\n"
			cat "${TMP_DIR}/run.${pid}"
			printf " --- END TRACE ---\n"
		} >&2
	fi
	rm -f "${TMP_DIR}/run.${pid}"

	return ${ret}
}

# convert any floating point number
# to integer, give a multiplier
# the result is stored in ${FLOAT2INT_RESULT}
# so that no fork is necessary
# the multiplier must be a power of 10
float2int() {
	local f m="$2" a b l v=($1)
	f=${v[0]}

	# the length of the multiplier - 1
	l=$((${#m} - 1))

	# check if the number is in scientific notation
	if [[ ${f} =~ ^[[:space:]]*(-)?[0-9.]+(e|E)(\+|-)[0-9]+ ]]; then
		# convert it to decimal
		# unfortunately, this fork cannot be avoided
		# if you know of a way to avoid it, please let me know
		f=$(printf "%0.${l}f" ${f})
	fi

	# split the floating point number
	# in integer (a) and decimal (b)
	a=${f/.*/}
	b=${f/*./}

	# if the integer part is missing
	# set it to zero
	[ -z "${a}" ] && a="0"

	# strip leading zeros from the integer part
	# base 10 conversion
	a=$((10#$a))

	# check the length of the decimal part
	# against the length of the multiplier
	if [ ${#b} -gt ${l} ]; then
		# too many digits - take the most significant
		b=${b:0:l}

	elif [ ${#b} -lt ${l} ]; then
		# too few digits - pad with zero on the right
		local z="00000000000000000000000" r=$((l - ${#b}))
		b="${b}${z:0:r}"
	fi

	# strip leading zeros from the decimal part
	# base 10 conversion
	b=$((10#$b))

	# store the result
	FLOAT2INT_RESULT=$(((a * m) + b))
}

# -----------------------------------------------------------------------------
# charts check functions

all_charts() {
	cd "$chartsd"
	[ $? -ne 0 ] && error "cannot cd to $chartsd" && return 1

	ls *.chart.sh | sed "s/\.chart\.sh$//g"
}

declare -A charts_enable_keyword=(
	['apache']="force"
	['cpu_apps']="force"
	['cpufreq']="force"
	['example']="force"
	['exim']="force"
	['hddtemp']="force"
	['load_average']="force"
	['mem_apps']="force"
	['mysql']="force"
	['nginx']="force"
	['phpfpm']="force"
	['postfix']="force"
	['sensors']="force"
	['squid']="force"
	['tomcat']="force"
)

declare -A obsolete_charts=(
  ['ap']="go.d/ap"
	['apache']="python.d.plugin module"
	['cpu_apps']="apps.plugin"
	['cpufreq']="proc plugin"
	['exim']="python.d.plugin module"
	['hddtemp']="python.d.plugin module"
	['load_average']="proc plugin"
	['mem_apps']="proc plugin"
	['mysql']="python.d.plugin module"
	['nginx']="python.d.plugin module"
	['phpfpm']="python.d.plugin module"
	['postfix']="python.d.plugin module"
	['squid']="python.d.plugin module"
	['tomcat']="python.d.plugin module"
)

all_enabled_charts() {
	local charts enabled required

	# find all enabled charts
	for chart in $(all_charts); do
		MODULE_NAME="${chart}"

		if [ -n "${obsolete_charts["$MODULE_NAME"]}" ]; then
		  debug "is replaced by ${obsolete_charts["$MODULE_NAME"]}, skipping it."
		  continue
		fi

		eval "enabled=\$$chart"
		if [ -z "${enabled}" ]; then
			enabled="${enable_all_charts}"
		fi

		required="${charts_enable_keyword[${chart}]}"
		[ -z "${required}" ] && required="yes"

		if [ ! "${enabled}" = "${required}" ]; then
			info "is disabled. Add a line with $chart=$required in '${NETDATA_USER_CONFIG_DIR}/${PROGRAM_NAME}.conf' to enable it (or remove the line that disables it)."
		else
			debug "is enabled for auto-detection."
			local charts="$charts $chart"
		fi
	done
	MODULE_NAME="main"

	local charts2=
	for chart in $charts; do
		MODULE_NAME="${chart}"

		# check the enabled charts
		local check="$(cat "$chartsd/$chart.chart.sh" | sed "s/^ \+//g" | grep "^$chart$charts_check()")"
		if [ -z "$check" ]; then
			error "module '$chart' does not seem to have a $chart$charts_check() function. Disabling it."
			continue
		fi

		local create="$(cat "$chartsd/$chart.chart.sh" | sed "s/^ \+//g" | grep "^$chart$charts_create()")"
		if [ -z "$create" ]; then
			error "module '$chart' does not seem to have a $chart$charts_create() function. Disabling it."
			continue
		fi

		local update="$(cat "$chartsd/$chart.chart.sh" | sed "s/^ \+//g" | grep "^$chart$charts_update()")"
		if [ -z "$update" ]; then
			error "module '$chart' does not seem to have a $chart$charts_update() function. Disabling it."
			continue
		fi

		# check its config
		#if [ -f "$userconfd/$chart.conf" ]
		#then
		#   if [ ! -z "$( cat "$userconfd/$chart.conf" | sed "s/^ \+//g" | grep -v "^$" | grep -v "^#" | grep -v "^$chart$charts_underscore" )" ]
		#   then
		#       error "module's $chart config $userconfd/$chart.conf should only have lines starting with $chart$charts_underscore . Disabling it."
		#       continue
		#   fi
		#fi

		#if [ $dryrunner -eq 1 ]
		#   then
		#   "$pluginsd/charts.d.dryrun-helper.sh" "$chart" "$chartsd/$chart.chart.sh" "$userconfd/$chart.conf" >/dev/null
		#   if [ $? -ne 0 ]
		#   then
		#       error "module's $chart did not pass the dry run check. This means it uses global variables not starting with $chart. Disabling it."
		#       continue
		#   fi
		#fi

		local charts2="$charts2 $chart"
	done
	MODULE_NAME="main"

	echo $charts2
	debug "enabled charts: $charts2"
}

# -----------------------------------------------------------------------------
# load the charts

suffix_retries="_retries"
suffix_update_every="_update_every"
active_charts=
for chart in $(all_enabled_charts); do
	MODULE_NAME="${chart}"

	debug "loading module: '$chartsd/$chart.chart.sh'"

	source "$chartsd/$chart.chart.sh"
	[ $? -ne 0 ] && warning "Module '$chartsd/$chart.chart.sh' loaded with errors."

	# first load the stock config
	if [ -f "$stockconfd/$chart.conf" ]; then
		debug "loading module configuration: '$stockconfd/$chart.conf'"
		source "$stockconfd/$chart.conf"
		[ $? -ne 0 ] && warning "Config file '$stockconfd/$chart.conf' loaded with errors."
	else
		debug "not found module configuration: '$stockconfd/$chart.conf'"
	fi

	# then load the user config (it overwrites the stock)
	if [ -f "$userconfd/$chart.conf" ]; then
		debug "loading module configuration: '$userconfd/$chart.conf'"
		source "$userconfd/$chart.conf"
		[ $? -ne 0 ] && warning "Config file '$userconfd/$chart.conf' loaded with errors."
	else
		debug "not found module configuration: '$userconfd/$chart.conf'"

		if [ -f "$olduserconfd/$chart.conf" ]; then
			# support for very old netdata that had the charts.d module configs in /etc/netdata
			info "loading module configuration from obsolete location: '$olduserconfd/$chart.conf'"
			source "$olduserconfd/$chart.conf"
			[ $? -ne 0 ] && warning "Config file '$olduserconfd/$chart.conf' loaded with errors."
		fi
	fi

	eval "dt=\$$chart$suffix_update_every"
	dt=$((dt + 1 - 1)) # make sure it is a number
	if [ $dt -lt $update_every ]; then
		eval "$chart$suffix_update_every=$update_every"
	fi

	$chart$charts_check
	if [ $? -eq 0 ]; then
		debug "module '$chart' activated"
		active_charts="$active_charts $chart"
	else
		error "module's '$chart' check() function reports failure."
	fi
done
MODULE_NAME="main"
debug "activated modules: $active_charts"

# -----------------------------------------------------------------------------
# check overwrites

# enable work time reporting
debug_time=
test $debug -eq 1 && debug_time=tellwork

# if we only need a specific chart, remove all the others
if [ ! -z "${chart_only}" ]; then
	debug "requested to run only for: '${chart_only}'"
	check_charts=
	for chart in $active_charts; do
		if [ "$chart" = "$chart_only" ]; then
			check_charts="$chart"
			break
		fi
	done
	active_charts="$check_charts"
fi
debug "activated charts: $active_charts"

# stop if we just need a pre-check
if [ $check -eq 1 ]; then
	info "CHECK RESULT"
	info "Will run the charts: $active_charts"
	exit 0
fi

# -----------------------------------------------------------------------------

cd "${TMP_DIR}" || exit 1

# -----------------------------------------------------------------------------
# create charts

run_charts=
for chart in $active_charts; do
	MODULE_NAME="${chart}"

	debug "calling '$chart$charts_create()'..."
	$chart$charts_create
	if [ $? -eq 0 ]; then
		run_charts="$run_charts $chart"
		debug "'$chart' initialized."
	else
		error "module's '$chart' function '$chart$charts_create()' reports failure."
	fi
done
MODULE_NAME="main"
debug "run_charts='$run_charts'"

# -----------------------------------------------------------------------------
# update dimensions

[ -z "$run_charts" ] && fatal "No charts to collect data from."

keepalive() {
  if [ ! -t 1 ] && ! printf "\n"; then
    chartsd_cleanup
  fi
}

declare -A charts_last_update=() charts_update_every=() charts_retries=() charts_next_update=() charts_run_counter=() charts_serial_failures=()
global_update() {
	local exit_at \
		c=0 dt ret last_ms exec_start_ms exec_end_ms \
		chart now_charts=() next_charts=($run_charts) \
		next_ms x seconds millis

	# return the current time in ms in $now_ms
	${current_time_ms}

	exit_at=$((now_ms + (restart_timeout * 1000)))

	for chart in $run_charts; do
		eval "charts_update_every[$chart]=\$$chart$suffix_update_every"
		test -z "${charts_update_every[$chart]}" && charts_update_every[$chart]=$update_every

		eval "charts_retries[$chart]=\$$chart$suffix_retries"
		test -z "${charts_retries[$chart]}" && charts_retries[$chart]=10

		charts_last_update[$chart]=$((now_ms - (now_ms % (charts_update_every[$chart] * 1000))))
		charts_next_update[$chart]=$((charts_last_update[$chart] + (charts_update_every[$chart] * 1000)))
		charts_run_counter[$chart]=0
		charts_serial_failures[$chart]=0

		echo "CHART netdata.plugin_chartsd_$chart '' 'Execution time for $chart plugin' 'milliseconds / run' charts.d netdata.plugin_charts area 145000 ${charts_update_every[$chart]} '' '' '$chart'"
		echo "DIMENSION run_time 'run time' absolute 1 1"
	done

	# the main loop
	while [ "${#next_charts[@]}" -gt 0 ]; do
	  keepalive

		c=$((c + 1))
		now_charts=("${next_charts[@]}")
		next_charts=()

		# return the current time in ms in $now_ms
		${current_time_ms}

		for chart in "${now_charts[@]}"; do
			MODULE_NAME="${chart}"

			if [ ${now_ms} -ge ${charts_next_update[$chart]} ]; then
				last_ms=${charts_last_update[$chart]}
				dt=$((now_ms - last_ms))

				charts_last_update[$chart]=${now_ms}

				while [ ${charts_next_update[$chart]} -lt ${now_ms} ]; do
					charts_next_update[$chart]=$((charts_next_update[$chart] + (charts_update_every[$chart] * 1000)))
				done

				# the first call should not give a duration
				# so that netdata calibrates to current time
				dt=$((dt * 1000))
				charts_run_counter[$chart]=$((charts_run_counter[$chart] + 1))
				if [ ${charts_run_counter[$chart]} -eq 1 ]; then
					dt=
				fi

				exec_start_ms=$now_ms
				$chart$charts_update $dt
				ret=$?

				# return the current time in ms in $now_ms
				${current_time_ms}
				exec_end_ms=$now_ms

				echo "BEGIN netdata.plugin_chartsd_$chart $dt"
				echo "SET run_time = $((exec_end_ms - exec_start_ms))"
				echo "END"

				if [ $ret -eq 0 ]; then
					charts_serial_failures[$chart]=0
					next_charts+=($chart)
				else
					charts_serial_failures[$chart]=$((charts_serial_failures[$chart] + 1))

					if [ ${charts_serial_failures[$chart]} -gt ${charts_retries[$chart]} ]; then
						error "module's '$chart' update() function reported failure ${charts_serial_failures[$chart]} times. Disabling it."
					else
						error "module's '$chart' update() function reports failure. Will keep trying for a while."
						next_charts+=($chart)
					fi
				fi
			else
				next_charts+=($chart)
			fi
		done
		MODULE_NAME="${chart}"

		# wait the time you are required to
		next_ms=$((now_ms + (update_every * 1000 * 100)))
		for x in "${charts_next_update[@]}"; do [ ${x} -lt ${next_ms} ] && next_ms=${x}; done
		next_ms=$((next_ms - now_ms))

		if [ ${LOOPSLEEPMS_HIGHRES} -eq 1 -a ${next_ms} -gt 0 ]; then
			next_ms=$((next_ms + current_time_ms_accuracy))
			seconds=$((next_ms / 1000))
			millis=$((next_ms % 1000))
			if [ ${millis} -lt 10 ]; then
				millis="00${millis}"
			elif [ ${millis} -lt 100 ]; then
				millis="0${millis}"
			fi

			debug "sleeping for ${seconds}.${millis} seconds."
			${mysleep} ${seconds}.${millis}
		else
			debug "sleeping for ${update_every} seconds."
			${mysleep} $update_every
		fi

		test ${now_ms} -ge ${exit_at} && exit 0
	done

	fatal "nothing left to do, exiting..."
}

global_update

Youez - 2016 - github.com/yon3zu
LinuXploit