PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i WWWWWW
Current Directory: /usr/lib/node_modules/pm2/node_modules/systeminformation/lib
Viewing File: /usr/lib/node_modules/pm2/node_modules/systeminformation/lib/filesystem.js
'use strict'; // @ts-check // ================================================================================== // filesystem.js // ---------------------------------------------------------------------------------- // Description: System Information - library // for Node.js // Copyright: (c) 2014 - 2025 // Author: Sebastian Hildebrandt // ---------------------------------------------------------------------------------- // License: MIT // ================================================================================== // 8. File System // ---------------------------------------------------------------------------------- const util = require('./util'); const fs = require('fs'); const exec = require('child_process').exec; const execSync = require('child_process').execSync; const execPromiseSave = util.promisifySave(require('child_process').exec); let _platform = process.platform; const _linux = (_platform === 'linux' || _platform === 'android'); const _darwin = (_platform === 'darwin'); const _windows = (_platform === 'win32'); const _freebsd = (_platform === 'freebsd'); const _openbsd = (_platform === 'openbsd'); const _netbsd = (_platform === 'netbsd'); const _sunos = (_platform === 'sunos'); let _fs_speed = {}; let _disk_io = {}; // -------------------------- // FS - mounted file systems function fsSize(drive, callback) { if (util.isFunction(drive)) { callback = drive; drive = ''; } let macOsDisks = []; let osMounts = []; function getmacOsFsType(fs) { if (!fs.startsWith('/')) { return 'NFS'; } const parts = fs.split('/'); const fsShort = parts[parts.length - 1]; const macOsDisksSingle = macOsDisks.filter(item => item.indexOf(fsShort) >= 0); if (macOsDisksSingle.length === 1 && macOsDisksSingle[0].indexOf('APFS') >= 0) { return 'APFS'; } return 'HFS'; } function isLinuxTmpFs(fs) { const linuxTmpFileSystems = ['rootfs', 'unionfs', 'squashfs', 'cramfs', 'initrd', 'initramfs', 'devtmpfs', 'tmpfs', 'udev', 'devfs', 'specfs', 'type', 'appimaged']; let result = false; linuxTmpFileSystems.forEach(linuxFs => { if (fs.toLowerCase().indexOf(linuxFs) >= 0) { result = true; } }); return result; } function filterLines(stdout) { let lines = stdout.toString().split('\n'); lines.shift(); if (stdout.toString().toLowerCase().indexOf('filesystem')) { let removeLines = 0; for (let i = 0; i < lines.length; i++) { if (lines[i] && lines[i].toLowerCase().startsWith('filesystem')) { removeLines = i; } } for (let i = 0; i < removeLines; i++) { lines.shift(); } } return lines; } function parseDf(lines) { let data = []; lines.forEach(function (line) { if (line !== '') { line = line.replace(/ +/g, ' ').split(' '); if (line && ((line[0].startsWith('/')) || (line[6] && line[6] === '/') || (line[0].indexOf('/') > 0) || (line[0].indexOf(':') === 1) || !_darwin && !isLinuxTmpFs(line[1]))) { const fs = line[0]; const fsType = ((_linux || _freebsd || _openbsd || _netbsd) ? line[1] : getmacOsFsType(line[0])); const size = parseInt(((_linux || _freebsd || _openbsd || _netbsd) ? line[2] : line[1])) * 1024; const used = parseInt(((_linux || _freebsd || _openbsd || _netbsd) ? line[3] : line[2])) * 1024; const available = parseInt(((_linux || _freebsd || _openbsd || _netbsd) ? line[4] : line[3])) * 1024; const use = parseFloat((100.0 * (used / (used + available))).toFixed(2)); let rw = osMounts && Object.keys(osMounts).length > 0 ? osMounts[fs] || false : null; line.splice(0, (_linux || _freebsd || _openbsd || _netbsd) ? 6 : 5); const mount = line.join(' '); if (!data.find(el => (el.fs === fs && el.type === fsType))) { data.push({ fs, type: fsType, size, used, available, use, mount, rw }); } } } }); return data; } return new Promise((resolve) => { process.nextTick(() => { let data = []; if (_linux || _freebsd || _openbsd || _netbsd || _darwin) { let cmd = ''; macOsDisks = []; osMounts = {}; if (_darwin) { cmd = 'df -kP'; try { macOsDisks = execSync('diskutil list').toString().split('\n').filter(line => { return !line.startsWith('/') && line.indexOf(':') > 0; }); execSync('mount').toString().split('\n').filter(line => { return line.startsWith('/'); }).forEach((line) => { osMounts[line.split(' ')[0]] = line.toLowerCase().indexOf('read-only') === -1; }); } catch (e) { util.noop(); } } if (_linux) { try { cmd = 'export LC_ALL=C; df -lkPTx squashfs; unset LC_ALL'; execSync('cat /proc/mounts 2>/dev/null', util.execOptsLinux).toString().split('\n').filter(line => { return line.startsWith('/'); }).forEach((line) => { osMounts[line.split(' ')[0]] = osMounts[line.split(' ')[0]] || false; if (line.toLowerCase().indexOf('/snap/') === -1) { osMounts[line.split(' ')[0]] = ((line.toLowerCase().indexOf('rw,') >= 0 || line.toLowerCase().indexOf(' rw ') >= 0)); } }); } catch (e) { util.noop(); } } if (_freebsd || _openbsd || _netbsd) { try { cmd = 'df -lkPT'; execSync('mount').toString().split('\n').forEach((line) => { osMounts[line.split(' ')[0]] = line.toLowerCase().indexOf('read-only') === -1; }); } catch (e) { util.noop(); } } exec(cmd, { maxBuffer: 1024 * 1024 }, function (error, stdout) { let lines = filterLines(stdout); data = parseDf(lines); if (drive) { data = data.filter(item => { return item.fs.toLowerCase().indexOf(drive.toLowerCase()) >= 0 || item.mount.toLowerCase().indexOf(drive.toLowerCase()) >= 0; }); } if ((!error || data.length) && stdout.toString().trim() !== '') { if (callback) { callback(data); } resolve(data); } else { exec('df -kPT', { maxBuffer: 1024 * 1024 }, function (error, stdout) { if (!error) { let lines = filterLines(stdout); data = parseDf(lines); } if (callback) { callback(data); } resolve(data); }); } }); } if (_sunos) { if (callback) { callback(data); } resolve(data); } if (_windows) { try { const cmd = `Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size ${drive ? '| where -property Caption -eq ' + drive : ''} | fl`; util.powerShell(cmd).then((stdout, error) => { if (!error) { let devices = stdout.toString().split(/\n\s*\n/); devices.forEach(function (device) { let lines = device.split('\r\n'); const size = util.toInt(util.getValue(lines, 'size', ':')); const free = util.toInt(util.getValue(lines, 'freespace', ':')); const caption = util.getValue(lines, 'caption', ':'); const rwValue = util.getValue(lines, 'access', ':'); const rw = rwValue ? (util.toInt(rwValue) !== 1) : null; if (size) { data.push({ fs: caption, type: util.getValue(lines, 'filesystem', ':'), size, used: size - free, available: free, use: parseFloat(((100.0 * (size - free)) / size).toFixed(2)), mount: caption, rw }); } }); } if (callback) { callback(data); } resolve(data); }); } catch (e) { if (callback) { callback(data); } resolve(data); } } }); }); } exports.fsSize = fsSize; // -------------------------- // FS - open files count function fsOpenFiles(callback) { return new Promise((resolve) => { process.nextTick(() => { const result = { max: null, allocated: null, available: null }; if (_freebsd || _openbsd || _netbsd || _darwin) { let cmd = 'sysctl -i kern.maxfiles kern.num_files kern.open_files'; exec(cmd, { maxBuffer: 1024 * 1024 }, function (error, stdout) { if (!error) { let lines = stdout.toString().split('\n'); result.max = parseInt(util.getValue(lines, 'kern.maxfiles', ':'), 10); result.allocated = parseInt(util.getValue(lines, 'kern.num_files', ':'), 10) || parseInt(util.getValue(lines, 'kern.open_files', ':'), 10); result.available = result.max - result.allocated; } if (callback) { callback(result); } resolve(result); }); } if (_linux) { fs.readFile('/proc/sys/fs/file-nr', function (error, stdout) { if (!error) { let lines = stdout.toString().split('\n'); if (lines[0]) { const parts = lines[0].replace(/\s+/g, ' ').split(' '); if (parts.length === 3) { result.allocated = parseInt(parts[0], 10); result.available = parseInt(parts[1], 10); result.max = parseInt(parts[2], 10); if (!result.available) { result.available = result.max - result.allocated; } } } if (callback) { callback(result); } resolve(result); } else { fs.readFile('/proc/sys/fs/file-max', function (error, stdout) { if (!error) { let lines = stdout.toString().split('\n'); if (lines[0]) { result.max = parseInt(lines[0], 10); } } if (callback) { callback(result); } resolve(result); }); } }); } if (_sunos) { if (callback) { callback(null); } resolve(null); } if (_windows) { if (callback) { callback(null); } resolve(null); } }); }); } exports.fsOpenFiles = fsOpenFiles; // -------------------------- // disks function parseBytes(s) { return parseInt(s.substr(s.indexOf(' (') + 2, s.indexOf(' Bytes)') - 10)); } function parseDevices(lines) { let devices = []; let i = 0; lines.forEach(line => { if (line.length > 0) { if (line[0] === '*') { i++; } else { let parts = line.split(':'); if (parts.length > 1) { if (!devices[i]) { devices[i] = { name: '', identifier: '', type: 'disk', fsType: '', mount: '', size: 0, physical: 'HDD', uuid: '', label: '', model: '', serial: '', removable: false, protocol: '', group: '', device: '' }; } parts[0] = parts[0].trim().toUpperCase().replace(/ +/g, ''); parts[1] = parts[1].trim(); if ('DEVICEIDENTIFIER' === parts[0]) { devices[i].identifier = parts[1]; } if ('DEVICENODE' === parts[0]) { devices[i].name = parts[1]; } if ('VOLUMENAME' === parts[0]) { if (parts[1].indexOf('Not applicable') === -1) { devices[i].label = parts[1]; } } if ('PROTOCOL' === parts[0]) { devices[i].protocol = parts[1]; } if ('DISKSIZE' === parts[0]) { devices[i].size = parseBytes(parts[1]); } if ('FILESYSTEMPERSONALITY' === parts[0]) { devices[i].fsType = parts[1]; } if ('MOUNTPOINT' === parts[0]) { devices[i].mount = parts[1]; } if ('VOLUMEUUID' === parts[0]) { devices[i].uuid = parts[1]; } if ('READ-ONLYMEDIA' === parts[0] && parts[1] === 'Yes') { devices[i].physical = 'CD/DVD'; } if ('SOLIDSTATE' === parts[0] && parts[1] === 'Yes') { devices[i].physical = 'SSD'; } if ('VIRTUAL' === parts[0]) { devices[i].type = 'virtual'; } if ('REMOVABLEMEDIA' === parts[0]) { devices[i].removable = (parts[1] === 'Removable'); } if ('PARTITIONTYPE' === parts[0]) { devices[i].type = 'part'; } if ('DEVICE/MEDIANAME' === parts[0]) { devices[i].model = parts[1]; } } } } }); return devices; } function parseBlk(lines) { let data = []; lines.filter(line => line !== '').forEach((line) => { try { line = decodeURIComponent(line.replace(/\\x/g, '%')); line = line.replace(/\\/g, '\\\\'); let disk = JSON.parse(line); data.push({ 'name': disk.name, 'type': disk.type, 'fsType': disk.fsType, 'mount': disk.mountpoint, 'size': parseInt(disk.size), 'physical': (disk.type === 'disk' ? (disk.rota === '0' ? 'SSD' : 'HDD') : (disk.type === 'rom' ? 'CD/DVD' : '')), 'uuid': disk.uuid, 'label': disk.label, 'model': (disk.model || '').trim(), 'serial': disk.serial, 'removable': disk.rm === '1', 'protocol': disk.tran, 'group': disk.group || '', }); } catch (e) { util.noop(); } }); data = util.unique(data); data = util.sortByKey(data, ['type', 'name']); return data; } function decodeMdabmData(lines) { const raid = util.getValue(lines, 'md_level', '='); const label = util.getValue(lines, 'md_name', '='); // <- get label info const uuid = util.getValue(lines, 'md_uuid', '='); // <- get uuid info const members = []; lines.forEach(line => { if (line.toLowerCase().startsWith('md_device_dev') && line.toLowerCase().indexOf('/dev/') > 0) { members.push(line.split('/dev/')[1]); } }); return { raid, label, uuid, members }; } function raidMatchLinux(data) { // for all block devices of type "raid%" let result = data; try { data.forEach(element => { if (element.type.startsWith('raid')) { const lines = execSync(`mdadm --export --detail /dev/${element.name}`, util.execOptsLinux).toString().split('\n'); const mdData = decodeMdabmData(lines); element.label = mdData.label; // <- assign label info element.uuid = mdData.uuid; // <- assign uuid info if (mdData.members && mdData.members.length && mdData.raid === element.type) { result = result.map(blockdevice => { if (blockdevice.fsType === 'linux_raid_member' && mdData.members.indexOf(blockdevice.name) >= 0) { blockdevice.group = element.name; } return blockdevice; }); } } }); } catch (e) { util.noop(); } return result; } function getDevicesLinux(data) { const result = []; data.forEach(element => { if (element.type.startsWith('disk')) { result.push(element.name); } }); return result; } function matchDevicesLinux(data) { let result = data; try { const devices = getDevicesLinux(data); result = result.map(blockdevice => { if (blockdevice.type.startsWith('part') || blockdevice.type.startsWith('disk')) { devices.forEach(element => { if (blockdevice.name.startsWith(element)) { blockdevice.device = '/dev/' + element; } }); } return blockdevice; }); } catch (e) { util.noop(); } return result; } function getDevicesMac(data) { const result = []; data.forEach(element => { if (element.type.startsWith('disk')) { result.push({ name: element.name, model: element.model, device: element.name }); } if (element.type.startsWith('virtual')) { let device = ''; result.forEach(e => { if (e.model === element.model) { device = e.device; } }); if (device) { result.push({ name: element.name, model: element.model, device }); } } }); return result; } function matchDevicesMac(data) { let result = data; try { const devices = getDevicesMac(data); result = result.map(blockdevice => { if (blockdevice.type.startsWith('part') || blockdevice.type.startsWith('disk') || blockdevice.type.startsWith('virtual')) { devices.forEach(element => { if (blockdevice.name.startsWith(element.name)) { blockdevice.device = element.device; } }); } return blockdevice; }); } catch (e) { util.noop(); } return result; } function getDevicesWin(diskDrives) { const result = []; diskDrives.forEach(element => { const lines = element.split('\r\n'); const device = util.getValue(lines, 'DeviceID', ':'); let partitions = element.split('@{DeviceID='); if (partitions.length > 1) { partitions = partitions.slice(1); partitions.forEach(partition => { result.push({ name: partition.split(';')[0].toUpperCase(), device }); }); } }); return result; } function matchDevicesWin(data, diskDrives) { const devices = getDevicesWin(diskDrives); data.map(element => { const filteresDevices = devices.filter((e) => { return e.name === element.name.toUpperCase(); }); if (filteresDevices.length > 0) { element.device = filteresDevices[0].device; } return element; }); return data; } function blkStdoutToObject(stdout) { return stdout.toString() .replace(/NAME=/g, '{"name":') .replace(/FSTYPE=/g, ',"fsType":') .replace(/TYPE=/g, ',"type":') .replace(/SIZE=/g, ',"size":') .replace(/MOUNTPOINT=/g, ',"mountpoint":') .replace(/UUID=/g, ',"uuid":') .replace(/ROTA=/g, ',"rota":') .replace(/RO=/g, ',"ro":') .replace(/RM=/g, ',"rm":') .replace(/TRAN=/g, ',"tran":') .replace(/SERIAL=/g, ',"serial":') .replace(/LABEL=/g, ',"label":') .replace(/MODEL=/g, ',"model":') .replace(/OWNER=/g, ',"owner":') .replace(/GROUP=/g, ',"group":') .replace(/\n/g, '}\n'); } function blockDevices(callback) { return new Promise((resolve) => { process.nextTick(() => { let data = []; if (_linux) { // see https://wiki.ubuntuusers.de/lsblk/ // exec("lsblk -bo NAME,TYPE,SIZE,FSTYPE,MOUNTPOINT,UUID,ROTA,RO,TRAN,SERIAL,LABEL,MODEL,OWNER,GROUP,MODE,ALIGNMENT,MIN-IO,OPT-IO,PHY-SEC,LOG-SEC,SCHED,RQ-SIZE,RA,WSAME", function (error, stdout) { const procLsblk1 = exec('lsblk -bPo NAME,TYPE,SIZE,FSTYPE,MOUNTPOINT,UUID,ROTA,RO,RM,TRAN,SERIAL,LABEL,MODEL,OWNER 2>/dev/null', { maxBuffer: 1024 * 1024 }, function (error, stdout) { if (!error) { let lines = blkStdoutToObject(stdout).split('\n'); data = parseBlk(lines); data = raidMatchLinux(data); data = matchDevicesLinux(data); if (callback) { callback(data); } resolve(data); } else { const procLsblk2 = exec('lsblk -bPo NAME,TYPE,SIZE,FSTYPE,MOUNTPOINT,UUID,ROTA,RO,RM,LABEL,MODEL,OWNER 2>/dev/null', { maxBuffer: 1024 * 1024 }, function (error, stdout) { if (!error) { let lines = blkStdoutToObject(stdout).split('\n'); data = parseBlk(lines); data = raidMatchLinux(data); } if (callback) { callback(data); } resolve(data); }); procLsblk2.on('error', function () { if (callback) { callback(data); } resolve(data); }); } }); procLsblk1.on('error', function () { if (callback) { callback(data); } resolve(data); }); } if (_darwin) { const procDskutil = exec('diskutil info -all', { maxBuffer: 1024 * 1024 }, function (error, stdout) { if (!error) { let lines = stdout.toString().split('\n'); // parse lines into temp array of devices data = parseDevices(lines); data = matchDevicesMac(data); } if (callback) { callback(data); } resolve(data); }); procDskutil.on('error', function () { if (callback) { callback(data); } resolve(data); }); } if (_sunos) { if (callback) { callback(data); } resolve(data); } if (_windows) { let drivetypes = ['Unknown', 'NoRoot', 'Removable', 'Local', 'Network', 'CD/DVD', 'RAM']; try { // util.wmic('logicaldisk get Caption,Description,DeviceID,DriveType,FileSystem,FreeSpace,Name,Size,VolumeName,VolumeSerialNumber /value').then((stdout, error) => { // util.powerShell('Get-CimInstance Win32_logicaldisk | select Caption,DriveType,Name,FileSystem,Size,VolumeSerialNumber,VolumeName | fl').then((stdout, error) => { const workload = []; workload.push(util.powerShell('Get-CimInstance -ClassName Win32_LogicalDisk | select Caption,DriveType,Name,FileSystem,Size,VolumeSerialNumber,VolumeName | fl')); workload.push(util.powerShell('Get-WmiObject -Class Win32_diskdrive | Select-Object -Property PNPDeviceId,DeviceID, Model, Size, @{L=\'Partitions\'; E={$_.GetRelated(\'Win32_DiskPartition\').GetRelated(\'Win32_LogicalDisk\') | Select-Object -Property DeviceID, VolumeName, Size, FreeSpace}} | fl')); util.promiseAll( workload ).then((res) => { let logicalDisks = res.results[0].toString().split(/\n\s*\n/); let diskDrives = res.results[1].toString().split(/\n\s*\n/); logicalDisks.forEach(function (device) { let lines = device.split('\r\n'); let drivetype = util.getValue(lines, 'drivetype', ':'); if (drivetype) { data.push({ name: util.getValue(lines, 'name', ':'), identifier: util.getValue(lines, 'caption', ':'), type: 'disk', fsType: util.getValue(lines, 'filesystem', ':').toLowerCase(), mount: util.getValue(lines, 'caption', ':'), size: util.getValue(lines, 'size', ':'), physical: (drivetype >= 0 && drivetype <= 6) ? drivetypes[drivetype] : drivetypes[0], uuid: util.getValue(lines, 'volumeserialnumber', ':'), label: util.getValue(lines, 'volumename', ':'), model: '', serial: util.getValue(lines, 'volumeserialnumber', ':'), removable: drivetype === '2', protocol: '', group: '', device: '' }); } }); // match devices data = matchDevicesWin(data, diskDrives); if (callback) { callback(data); } resolve(data); }); } catch (e) { if (callback) { callback(data); } resolve(data); } } if (_freebsd || _openbsd || _netbsd) { // will follow if (callback) { callback(null); } resolve(null); } }); }); } exports.blockDevices = blockDevices; // -------------------------- // FS - speed function calcFsSpeed(rx, wx) { let result = { rx: 0, wx: 0, tx: 0, rx_sec: null, wx_sec: null, tx_sec: null, ms: 0 }; if (_fs_speed && _fs_speed.ms) { result.rx = rx; result.wx = wx; result.tx = result.rx + result.wx; result.ms = Date.now() - _fs_speed.ms; result.rx_sec = (result.rx - _fs_speed.bytes_read) / (result.ms / 1000); result.wx_sec = (result.wx - _fs_speed.bytes_write) / (result.ms / 1000); result.tx_sec = result.rx_sec + result.wx_sec; _fs_speed.rx_sec = result.rx_sec; _fs_speed.wx_sec = result.wx_sec; _fs_speed.tx_sec = result.tx_sec; _fs_speed.bytes_read = result.rx; _fs_speed.bytes_write = result.wx; _fs_speed.bytes_overall = result.rx + result.wx; _fs_speed.ms = Date.now(); _fs_speed.last_ms = result.ms; } else { result.rx = rx; result.wx = wx; result.tx = result.rx + result.wx; _fs_speed.rx_sec = null; _fs_speed.wx_sec = null; _fs_speed.tx_sec = null; _fs_speed.bytes_read = result.rx; _fs_speed.bytes_write = result.wx; _fs_speed.bytes_overall = result.rx + result.wx; _fs_speed.ms = Date.now(); _fs_speed.last_ms = 0; } return result; } function fsStats(callback) { return new Promise((resolve) => { process.nextTick(() => { if (_windows || _freebsd || _openbsd || _netbsd || _sunos) { return resolve(null); } let result = { rx: 0, wx: 0, tx: 0, rx_sec: null, wx_sec: null, tx_sec: null, ms: 0 }; let rx = 0; let wx = 0; if ((_fs_speed && !_fs_speed.ms) || (_fs_speed && _fs_speed.ms && Date.now() - _fs_speed.ms >= 500)) { if (_linux) { // exec("df -k | grep /dev/", function(error, stdout) { const procLsblk = exec('lsblk -r 2>/dev/null | grep /', { maxBuffer: 1024 * 1024 }, function (error, stdout) { if (!error) { let lines = stdout.toString().split('\n'); let fs_filter = []; lines.forEach(function (line) { if (line !== '') { line = line.trim().split(' '); if (fs_filter.indexOf(line[0]) === -1) { fs_filter.push(line[0]); } } }); let output = fs_filter.join('|'); const procCat = exec('cat /proc/diskstats | egrep "' + output + '"', { maxBuffer: 1024 * 1024 }, function (error, stdout) { if (!error) { let lines = stdout.toString().split('\n'); lines.forEach(function (line) { line = line.trim(); if (line !== '') { line = line.replace(/ +/g, ' ').split(' '); rx += parseInt(line[5]) * 512; wx += parseInt(line[9]) * 512; } }); result = calcFsSpeed(rx, wx); } if (callback) { callback(result); } resolve(result); }); procCat.on('error', function () { if (callback) { callback(result); } resolve(result); }); } else { if (callback) { callback(result); } resolve(result); } }); procLsblk.on('error', function () { if (callback) { callback(result); } resolve(result); }); } if (_darwin) { const procIoreg = exec('ioreg -c IOBlockStorageDriver -k Statistics -r -w0 | sed -n "/IOBlockStorageDriver/,/Statistics/p" | grep "Statistics" | tr -cd "01234567890,\n"', { maxBuffer: 1024 * 1024 }, function (error, stdout) { if (!error) { let lines = stdout.toString().split('\n'); lines.forEach(function (line) { line = line.trim(); if (line !== '') { line = line.split(','); rx += parseInt(line[2]); wx += parseInt(line[9]); } }); result = calcFsSpeed(rx, wx); } if (callback) { callback(result); } resolve(result); }); procIoreg.on('error', function () { if (callback) { callback(result); } resolve(result); }); } } else { result.ms = _fs_speed.last_ms; result.rx = _fs_speed.bytes_read; result.wx = _fs_speed.bytes_write; result.tx = _fs_speed.bytes_read + _fs_speed.bytes_write; result.rx_sec = _fs_speed.rx_sec; result.wx_sec = _fs_speed.wx_sec; result.tx_sec = _fs_speed.tx_sec; if (callback) { callback(result); } resolve(result); } }); }); } exports.fsStats = fsStats; function calcDiskIO(rIO, wIO, rWaitTime, wWaitTime, tWaitTime) { let result = { rIO: 0, wIO: 0, tIO: 0, rIO_sec: null, wIO_sec: null, tIO_sec: null, rWaitTime: 0, wWaitTime: 0, tWaitTime: 0, rWaitPercent: null, wWaitPercent: null, tWaitPercent: null, ms: 0 }; if (_disk_io && _disk_io.ms) { result.rIO = rIO; result.wIO = wIO; result.tIO = rIO + wIO; result.ms = Date.now() - _disk_io.ms; result.rIO_sec = (result.rIO - _disk_io.rIO) / (result.ms / 1000); result.wIO_sec = (result.wIO - _disk_io.wIO) / (result.ms / 1000); result.tIO_sec = result.rIO_sec + result.wIO_sec; result.rWaitTime = rWaitTime; result.wWaitTime = wWaitTime; result.tWaitTime = tWaitTime; result.rWaitPercent = (result.rWaitTime - _disk_io.rWaitTime) * 100 / (result.ms); result.wWaitPercent = (result.wWaitTime - _disk_io.wWaitTime) * 100 / (result.ms); result.tWaitPercent = (result.tWaitTime - _disk_io.tWaitTime) * 100 / (result.ms); _disk_io.rIO = rIO; _disk_io.wIO = wIO; _disk_io.rIO_sec = result.rIO_sec; _disk_io.wIO_sec = result.wIO_sec; _disk_io.tIO_sec = result.tIO_sec; _disk_io.rWaitTime = rWaitTime; _disk_io.wWaitTime = wWaitTime; _disk_io.tWaitTime = tWaitTime; _disk_io.rWaitPercent = result.rWaitPercent; _disk_io.wWaitPercent = result.wWaitPercent; _disk_io.tWaitPercent = result.tWaitPercent; _disk_io.last_ms = result.ms; _disk_io.ms = Date.now(); } else { result.rIO = rIO; result.wIO = wIO; result.tIO = rIO + wIO; result.rWaitTime = rWaitTime; result.wWaitTime = wWaitTime; result.tWaitTime = tWaitTime; _disk_io.rIO = rIO; _disk_io.wIO = wIO; _disk_io.rIO_sec = null; _disk_io.wIO_sec = null; _disk_io.tIO_sec = null; _disk_io.rWaitTime = rWaitTime; _disk_io.wWaitTime = wWaitTime; _disk_io.tWaitTime = tWaitTime; _disk_io.rWaitPercent = null; _disk_io.wWaitPercent = null; _disk_io.tWaitPercent = null; _disk_io.last_ms = 0; _disk_io.ms = Date.now(); } return result; } function disksIO(callback) { return new Promise((resolve) => { process.nextTick(() => { if (_windows) { return resolve(null); } if (_sunos) { return resolve(null); } let result = { rIO: 0, wIO: 0, tIO: 0, rIO_sec: null, wIO_sec: null, tIO_sec: null, rWaitTime: 0, wWaitTime: 0, tWaitTime: 0, rWaitPercent: null, wWaitPercent: null, tWaitPercent: null, ms: 0 }; let rIO = 0; let wIO = 0; let rWaitTime = 0; let wWaitTime = 0; let tWaitTime = 0; if ((_disk_io && !_disk_io.ms) || (_disk_io && _disk_io.ms && Date.now() - _disk_io.ms >= 500)) { if (_linux || _freebsd || _openbsd || _netbsd) { // prints Block layer statistics for all mounted volumes // var cmd = "for mount in `lsblk | grep / | sed -r 's/│ └─//' | cut -d ' ' -f 1`; do cat /sys/block/$mount/stat | sed -r 's/ +/;/g' | sed -r 's/^;//'; done"; // var cmd = "for mount in `lsblk | grep / | sed 's/[│└─├]//g' | awk '{$1=$1};1' | cut -d ' ' -f 1 | sort -u`; do cat /sys/block/$mount/stat | sed -r 's/ +/;/g' | sed -r 's/^;//'; done"; let cmd = 'for mount in `lsblk 2>/dev/null | grep " disk " | sed "s/[│└─├]//g" | awk \'{$1=$1};1\' | cut -d " " -f 1 | sort -u`; do cat /sys/block/$mount/stat | sed -r "s/ +/;/g" | sed -r "s/^;//"; done'; exec(cmd, { maxBuffer: 1024 * 1024 }, function (error, stdout) { if (!error) { let lines = stdout.split('\n'); lines.forEach(function (line) { // ignore empty lines if (!line) { return; } // sum r/wIO of all disks to compute all disks IO let stats = line.split(';'); rIO += parseInt(stats[0]); wIO += parseInt(stats[4]); rWaitTime += parseInt(stats[3]); wWaitTime += parseInt(stats[7]); tWaitTime += parseInt(stats[10]); }); result = calcDiskIO(rIO, wIO, rWaitTime, wWaitTime, tWaitTime); if (callback) { callback(result); } resolve(result); } else { if (callback) { callback(result); } resolve(result); } }); } if (_darwin) { exec('ioreg -c IOBlockStorageDriver -k Statistics -r -w0 | sed -n "/IOBlockStorageDriver/,/Statistics/p" | grep "Statistics" | tr -cd "01234567890,\n"', { maxBuffer: 1024 * 1024 }, function (error, stdout) { if (!error) { let lines = stdout.toString().split('\n'); lines.forEach(function (line) { line = line.trim(); if (line !== '') { line = line.split(','); rIO += parseInt(line[10]); wIO += parseInt(line[0]); } }); result = calcDiskIO(rIO, wIO, rWaitTime, wWaitTime, tWaitTime); } if (callback) { callback(result); } resolve(result); }); } } else { result.rIO = _disk_io.rIO; result.wIO = _disk_io.wIO; result.tIO = _disk_io.rIO + _disk_io.wIO; result.ms = _disk_io.last_ms; result.rIO_sec = _disk_io.rIO_sec; result.wIO_sec = _disk_io.wIO_sec; result.tIO_sec = _disk_io.tIO_sec; result.rWaitTime = _disk_io.rWaitTime; result.wWaitTime = _disk_io.wWaitTime; result.tWaitTime = _disk_io.tWaitTime; result.rWaitPercent = _disk_io.rWaitPercent; result.wWaitPercent = _disk_io.wWaitPercent; result.tWaitPercent = _disk_io.tWaitPercent; if (callback) { callback(result); } resolve(result); } }); }); } exports.disksIO = disksIO; function diskLayout(callback) { function getVendorFromModel(model) { const diskManufacturers = [ { pattern: 'WESTERN.*', manufacturer: 'Western Digital' }, { pattern: '^WDC.*', manufacturer: 'Western Digital' }, { pattern: 'WD.*', manufacturer: 'Western Digital' }, { pattern: 'TOSHIBA.*', manufacturer: 'Toshiba' }, { pattern: 'HITACHI.*', manufacturer: 'Hitachi' }, { pattern: '^IC.*', manufacturer: 'Hitachi' }, { pattern: '^HTS.*', manufacturer: 'Hitachi' }, { pattern: 'SANDISK.*', manufacturer: 'SanDisk' }, { pattern: 'KINGSTON.*', manufacturer: 'Kingston Technology' }, { pattern: '^SONY.*', manufacturer: 'Sony' }, { pattern: 'TRANSCEND.*', manufacturer: 'Transcend' }, { pattern: 'SAMSUNG.*', manufacturer: 'Samsung' }, { pattern: '^ST(?!I\\ ).*', manufacturer: 'Seagate' }, { pattern: '^STI\\ .*', manufacturer: 'SimpleTech' }, { pattern: '^D...-.*', manufacturer: 'IBM' }, { pattern: '^IBM.*', manufacturer: 'IBM' }, { pattern: '^FUJITSU.*', manufacturer: 'Fujitsu' }, { pattern: '^MP.*', manufacturer: 'Fujitsu' }, { pattern: '^MK.*', manufacturer: 'Toshiba' }, { pattern: 'MAXTO.*', manufacturer: 'Maxtor' }, { pattern: 'PIONEER.*', manufacturer: 'Pioneer' }, { pattern: 'PHILIPS.*', manufacturer: 'Philips' }, { pattern: 'QUANTUM.*', manufacturer: 'Quantum Technology' }, { pattern: 'FIREBALL.*', manufacturer: 'Quantum Technology' }, { pattern: '^VBOX.*', manufacturer: 'VirtualBox' }, { pattern: 'CORSAIR.*', manufacturer: 'Corsair Components' }, { pattern: 'CRUCIAL.*', manufacturer: 'Crucial' }, { pattern: 'ECM.*', manufacturer: 'ECM' }, { pattern: 'INTEL.*', manufacturer: 'INTEL' }, { pattern: 'EVO.*', manufacturer: 'Samsung' }, { pattern: 'APPLE.*', manufacturer: 'Apple' }, ]; let result = ''; if (model) { model = model.toUpperCase(); diskManufacturers.forEach((manufacturer) => { const re = RegExp(manufacturer.pattern); if (re.test(model)) { result = manufacturer.manufacturer; } }); } return result; } return new Promise((resolve) => { process.nextTick(() => { const commitResult = res => { for (let i = 0; i < res.length; i++) { delete res[i].BSDName; } if (callback) { callback(res); } resolve(res); }; let result = []; let cmd = ''; if (_linux) { let cmdFullSmart = ''; exec('export LC_ALL=C; lsblk -ablJO 2>/dev/null; unset LC_ALL', { maxBuffer: 1024 * 1024 }, function (error, stdout) { if (!error) { try { const out = stdout.toString().trim(); let devices = []; try { const outJSON = JSON.parse(out); if (outJSON && {}.hasOwnProperty.call(outJSON, 'blockdevices')) { devices = outJSON.blockdevices.filter(item => { return (item.type === 'disk') && item.size > 0 && (item.model !== null || (item.mountpoint === null && item.label === null && item.fstype === null && item.parttype === null && item.path && item.path.indexOf('/ram') !== 0 && item.path.indexOf('/loop') !== 0 && item['disc-max'] && item['disc-max'] !== 0)); }); } } catch (e) { // fallback to older version of lsblk try { const out2 = execSync('export LC_ALL=C; lsblk -bPo NAME,TYPE,SIZE,FSTYPE,MOUNTPOINT,UUID,ROTA,RO,RM,LABEL,MODEL,OWNER,GROUP 2>/dev/null; unset LC_ALL', util.execOptsLinux).toString(); let lines = blkStdoutToObject(out2).split('\n'); const data = parseBlk(lines); devices = data.filter(item => { return (item.type === 'disk') && item.size > 0 && ((item.model !== null && item.model !== '') || (item.mount === '' && item.label === '' && item.fsType === '')); }); } catch (e) { util.noop(); } } devices.forEach((device) => { let mediumType = ''; const BSDName = '/dev/' + device.name; const logical = device.name; try { mediumType = execSync('cat /sys/block/' + logical + '/queue/rotational 2>/dev/null', util.execOptsLinux).toString().split('\n')[0]; } catch (e) { util.noop(); } let interfaceType = device.tran ? device.tran.toUpperCase().trim() : ''; if (interfaceType === 'NVME') { mediumType = '2'; interfaceType = 'PCIe'; } result.push({ device: BSDName, type: (mediumType === '0' ? 'SSD' : (mediumType === '1' ? 'HD' : (mediumType === '2' ? 'NVMe' : (device.model && device.model.indexOf('SSD') > -1 ? 'SSD' : (device.model && device.model.indexOf('NVM') > -1 ? 'NVMe' : 'HD'))))), name: device.model || '', vendor: getVendorFromModel(device.model) || (device.vendor ? device.vendor.trim() : ''), size: device.size || 0, bytesPerSector: null, totalCylinders: null, totalHeads: null, totalSectors: null, totalTracks: null, tracksPerCylinder: null, sectorsPerTrack: null, firmwareRevision: device.rev ? device.rev.trim() : '', serialNum: device.serial ? device.serial.trim() : '', interfaceType: interfaceType, smartStatus: 'unknown', temperature: null, BSDName: BSDName }); cmd += `printf "\n${BSDName}|"; smartctl -H ${BSDName} | grep overall;`; cmdFullSmart += `${cmdFullSmart ? 'printf ",";' : ''}smartctl -a -j ${BSDName};`; }); } catch (e) { util.noop(); } } // check S.M.A.R.T. status if (cmdFullSmart) { exec(cmdFullSmart, { maxBuffer: 1024 * 1024 }, function (error, stdout) { try { const data = JSON.parse(`[${stdout}]`); data.forEach(disk => { const diskBSDName = disk.smartctl.argv[disk.smartctl.argv.length - 1]; for (let i = 0; i < result.length; i++) { if (result[i].BSDName === diskBSDName) { result[i].smartStatus = (disk.smart_status.passed ? 'Ok' : (disk.smart_status.passed === false ? 'Predicted Failure' : 'unknown')); if (disk.temperature && disk.temperature.current) { result[i].temperature = disk.temperature.current; } result[i].smartData = disk; } } }); commitResult(result); } catch (e) { if (cmd) { cmd = cmd + 'printf "\n"'; exec(cmd, { maxBuffer: 1024 * 1024 }, function (error, stdout) { let lines = stdout.toString().split('\n'); lines.forEach(line => { if (line) { let parts = line.split('|'); if (parts.length === 2) { let BSDName = parts[0]; parts[1] = parts[1].trim(); let parts2 = parts[1].split(':'); if (parts2.length === 2) { parts2[1] = parts2[1].trim(); let status = parts2[1].toLowerCase(); for (let i = 0; i < result.length; i++) { if (result[i].BSDName === BSDName) { result[i].smartStatus = (status === 'passed' ? 'Ok' : (status === 'failed!' ? 'Predicted Failure' : 'unknown')); } } } } } }); commitResult(result); }); } else { commitResult(result); } } }); } else { commitResult(result); } }); } if (_freebsd || _openbsd || _netbsd) { if (callback) { callback(result); } resolve(result); } if (_sunos) { if (callback) { callback(result); } resolve(result); } if (_darwin) { exec('system_profiler SPSerialATADataType SPNVMeDataType SPUSBDataType', { maxBuffer: 1024 * 1024 }, function (error, stdout) { if (!error) { // split by type: let lines = stdout.toString().split('\n'); let linesSATA = []; let linesNVMe = []; let linesUSB = []; let dataType = 'SATA'; lines.forEach(line => { if (line === 'NVMExpress:') { dataType = 'NVMe'; } else if (line === 'USB:') { dataType = 'USB'; } else if (line === 'SATA/SATA Express:') { dataType = 'SATA'; } else if (dataType === 'SATA') { linesSATA.push(line); } else if (dataType === 'NVMe') { linesNVMe.push(line); } else if (dataType === 'USB') { linesUSB.push(line); } }); try { // Serial ATA Drives let devices = linesSATA.join('\n').split(' Physical Interconnect: '); devices.shift(); devices.forEach(function (device) { device = 'InterfaceType: ' + device; let lines = device.split('\n'); const mediumType = util.getValue(lines, 'Medium Type', ':', true).trim(); const sizeStr = util.getValue(lines, 'capacity', ':', true).trim(); const BSDName = util.getValue(lines, 'BSD Name', ':', true).trim(); if (sizeStr) { let sizeValue = 0; if (sizeStr.indexOf('(') >= 0) { sizeValue = parseInt(sizeStr.match(/\(([^)]+)\)/)[1].replace(/\./g, '').replace(/,/g, '').replace(/\s/g, '')); } if (!sizeValue) { sizeValue = parseInt(sizeStr); } if (sizeValue) { const smartStatusString = util.getValue(lines, 'S.M.A.R.T. status', ':', true).trim().toLowerCase(); result.push({ device: BSDName, type: mediumType.startsWith('Solid') ? 'SSD' : 'HD', name: util.getValue(lines, 'Model', ':', true).trim(), vendor: getVendorFromModel(util.getValue(lines, 'Model', ':', true).trim()) || util.getValue(lines, 'Manufacturer', ':', true), size: sizeValue, bytesPerSector: null, totalCylinders: null, totalHeads: null, totalSectors: null, totalTracks: null, tracksPerCylinder: null, sectorsPerTrack: null, firmwareRevision: util.getValue(lines, 'Revision', ':', true).trim(), serialNum: util.getValue(lines, 'Serial Number', ':', true).trim(), interfaceType: util.getValue(lines, 'InterfaceType', ':', true).trim(), smartStatus: smartStatusString === 'verified' ? 'OK' : smartStatusString || 'unknown', temperature: null, BSDName: BSDName }); cmd = cmd + 'printf "\n' + BSDName + '|"; diskutil info /dev/' + BSDName + ' | grep SMART;'; } } }); } catch (e) { util.noop(); } // NVME Drives try { let devices = linesNVMe.join('\n').split('\n\n Capacity:'); devices.shift(); devices.forEach(function (device) { device = '!Capacity: ' + device; let lines = device.split('\n'); const linkWidth = util.getValue(lines, 'link width', ':', true).trim(); const sizeStr = util.getValue(lines, '!capacity', ':', true).trim(); const BSDName = util.getValue(lines, 'BSD Name', ':', true).trim(); if (sizeStr) { let sizeValue = 0; if (sizeStr.indexOf('(') >= 0) { sizeValue = parseInt(sizeStr.match(/\(([^)]+)\)/)[1].replace(/\./g, '').replace(/,/g, '').replace(/\s/g, '')); } if (!sizeValue) { sizeValue = parseInt(sizeStr); } if (sizeValue) { const smartStatusString = util.getValue(lines, 'S.M.A.R.T. status', ':', true).trim().toLowerCase(); result.push({ device: BSDName, type: 'NVMe', name: util.getValue(lines, 'Model', ':', true).trim(), vendor: getVendorFromModel(util.getValue(lines, 'Model', ':', true).trim()), size: sizeValue, bytesPerSector: null, totalCylinders: null, totalHeads: null, totalSectors: null, totalTracks: null, tracksPerCylinder: null, sectorsPerTrack: null, firmwareRevision: util.getValue(lines, 'Revision', ':', true).trim(), serialNum: util.getValue(lines, 'Serial Number', ':', true).trim(), interfaceType: ('PCIe ' + linkWidth).trim(), smartStatus: smartStatusString === 'verified' ? 'OK' : smartStatusString || 'unknown', temperature: null, BSDName: BSDName }); cmd = cmd + 'printf "\n' + BSDName + '|"; diskutil info /dev/' + BSDName + ' | grep SMART;'; } } }); } catch (e) { util.noop(); } // USB Drives try { let devices = linesUSB.join('\n').replaceAll('Media:\n ', 'Model:').split('\n\n Product ID:'); devices.shift(); devices.forEach(function (device) { let lines = device.split('\n'); const sizeStr = util.getValue(lines, 'Capacity', ':', true).trim(); const BSDName = util.getValue(lines, 'BSD Name', ':', true).trim(); if (sizeStr) { let sizeValue = 0; if (sizeStr.indexOf('(') >= 0) { sizeValue = parseInt(sizeStr.match(/\(([^)]+)\)/)[1].replace(/\./g, '').replace(/,/g, '').replace(/\s/g, '')); } if (!sizeValue) { sizeValue = parseInt(sizeStr); } if (sizeValue) { const smartStatusString = util.getValue(lines, 'S.M.A.R.T. status', ':', true).trim().toLowerCase(); result.push({ device: BSDName, type: 'USB', name: util.getValue(lines, 'Model', ':', true).trim().replaceAll(':', ''), vendor: getVendorFromModel(util.getValue(lines, 'Model', ':', true).trim()), size: sizeValue, bytesPerSector: null, totalCylinders: null, totalHeads: null, totalSectors: null, totalTracks: null, tracksPerCylinder: null, sectorsPerTrack: null, firmwareRevision: util.getValue(lines, 'Revision', ':', true).trim(), serialNum: util.getValue(lines, 'Serial Number', ':', true).trim(), interfaceType: 'USB', smartStatus: smartStatusString === 'verified' ? 'OK' : smartStatusString || 'unknown', temperature: null, BSDName: BSDName }); cmd = cmd + 'printf "\n' + BSDName + '|"; diskutil info /dev/' + BSDName + ' | grep SMART;'; } } }); } catch (e) { util.noop(); } if (cmd) { cmd = cmd + 'printf "\n"'; exec(cmd, { maxBuffer: 1024 * 1024 }, function (error, stdout) { let lines = stdout.toString().split('\n'); lines.forEach(line => { if (line) { let parts = line.split('|'); if (parts.length === 2) { let BSDName = parts[0]; parts[1] = parts[1].trim(); let parts2 = parts[1].split(':'); if (parts2.length === 2) { parts2[1] = parts2[1].trim(); let status = parts2[1].toLowerCase(); for (let i = 0; i < result.length; i++) { if (result[i].BSDName === BSDName) { result[i].smartStatus = (status === 'not supported' ? 'not supported' : (status === 'verified' ? 'Ok' : (status === 'failing' ? 'Predicted Failure' : 'unknown'))); } } } } } }); commitResult(result); }); } else { commitResult(result); } } else { commitResult(result); } }); } if (_windows) { try { const workload = []; workload.push(util.powerShell('Get-CimInstance Win32_DiskDrive | select Caption,Size,Status,PNPDeviceId,DeviceId,BytesPerSector,TotalCylinders,TotalHeads,TotalSectors,TotalTracks,TracksPerCylinder,SectorsPerTrack,FirmwareRevision,SerialNumber,InterfaceType | fl')); workload.push(util.powerShell('Get-PhysicalDisk | select BusType,MediaType,FriendlyName,Model,SerialNumber,Size | fl')); if (util.smartMonToolsInstalled()) { try { const smartDev = JSON.parse(execSync('smartctl --scan -j').toString()); if (smartDev && smartDev.devices && smartDev.devices.length > 0) { smartDev.devices.forEach((dev) => { workload.push(execPromiseSave(`smartctl -j -a ${dev.name}`, util.execOptsWin)); }); } } catch (e) { util.noop(); } } util.promiseAll( workload ).then((data) => { let devices = data.results[0].toString().split(/\n\s*\n/); devices.forEach(function (device) { let lines = device.split('\r\n'); const size = util.getValue(lines, 'Size', ':').trim(); const status = util.getValue(lines, 'Status', ':').trim().toLowerCase(); if (size) { result.push({ device: util.getValue(lines, 'DeviceId', ':'), // changed from PNPDeviceId to DeviceID (be be able to match devices) type: device.indexOf('SSD') > -1 ? 'SSD' : 'HD', // just a starting point ... better: MSFT_PhysicalDisk - Media Type ... see below name: util.getValue(lines, 'Caption', ':'), vendor: getVendorFromModel(util.getValue(lines, 'Caption', ':', true).trim()), size: parseInt(size), bytesPerSector: parseInt(util.getValue(lines, 'BytesPerSector', ':')), totalCylinders: parseInt(util.getValue(lines, 'TotalCylinders', ':')), totalHeads: parseInt(util.getValue(lines, 'TotalHeads', ':')), totalSectors: parseInt(util.getValue(lines, 'TotalSectors', ':')), totalTracks: parseInt(util.getValue(lines, 'TotalTracks', ':')), tracksPerCylinder: parseInt(util.getValue(lines, 'TracksPerCylinder', ':')), sectorsPerTrack: parseInt(util.getValue(lines, 'SectorsPerTrack', ':')), firmwareRevision: util.getValue(lines, 'FirmwareRevision', ':').trim(), serialNum: util.getValue(lines, 'SerialNumber', ':').trim(), interfaceType: util.getValue(lines, 'InterfaceType', ':').trim(), smartStatus: (status === 'ok' ? 'Ok' : (status === 'degraded' ? 'Degraded' : (status === 'pred fail' ? 'Predicted Failure' : 'Unknown'))), temperature: null, }); } }); devices = data.results[1].split(/\n\s*\n/); devices.forEach(function (device) { let lines = device.split('\r\n'); const serialNum = util.getValue(lines, 'SerialNumber', ':').trim(); const name = util.getValue(lines, 'FriendlyName', ':').trim().replace('Msft ', 'Microsoft'); const size = util.getValue(lines, 'Size', ':').trim(); const model = util.getValue(lines, 'Model', ':').trim(); const interfaceType = util.getValue(lines, 'BusType', ':').trim(); let mediaType = util.getValue(lines, 'MediaType', ':').trim(); if (mediaType === '3' || mediaType === 'HDD') { mediaType = 'HD'; } if (mediaType === '4') { mediaType = 'SSD'; } if (mediaType === '5') { mediaType = 'SCM'; } if (mediaType === 'Unspecified' && (model.toLowerCase().indexOf('virtual') > -1 || model.toLowerCase().indexOf('vbox') > -1)) { mediaType = 'Virtual'; } if (size) { let i = util.findObjectByKey(result, 'serialNum', serialNum); if (i === -1 || serialNum === '') { i = util.findObjectByKey(result, 'name', name); } if (i != -1) { result[i].type = mediaType; result[i].interfaceType = interfaceType; } } }); // S.M.A.R.T data.results.shift(); data.results.shift(); if (data.results.length) { data.results.forEach((smartStr) => { try { const smartData = JSON.parse(smartStr); if (smartData.serial_number) { const serialNum = smartData.serial_number; let i = util.findObjectByKey(result, 'serialNum', serialNum); if (i != -1) { result[i].smartStatus = (smartData.smart_status && smartData.smart_status.passed ? 'Ok' : (smartData.smart_status && smartData.smart_status.passed === false ? 'Predicted Failure' : 'unknown')); if (smartData.temperature && smartData.temperature.current) { result[i].temperature = smartData.temperature.current; } result[i].smartData = smartData; } } } catch (e) { util.noop(); } }); } if (callback) { callback(result); } resolve(result); }); } catch (e) { if (callback) { callback(result); } resolve(result); } } }); }); } exports.diskLayout = diskLayout;