Advertisement
Guest User

dz0ny

a guest
Jul 29th, 2008
455
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 25.35 KB | None | 0 0
  1. #!/opt/bin/bash
  2. ####################################################################
  3. # rrdstorm v1.0 (c) 2007 http://xlife.zuavra.net && cupacup at wl500g.info
  4. # Published under the terms of the GNU General Public License v2.
  5. # This program simplifies the use of rrdtool and rrdupdate.
  6. # The vanilla version is tweaked by default for use on the
  7. # For running this script you should also have bash installed, and check for right grep functions in disk stats
  8. ####################################################################
  9. VERSION="wl500gpv2"
  10. DATE=$(date '+%x %R')
  11. ####################################################################
  12.  
  13. #-------------------------------------------------------------------
  14. # configuration
  15. #-------------------------------------------------------------------
  16.  
  17. RRDTOOL=/opt/bin/rrdtool
  18. RRDUPDATE=/opt/bin/rrdupdate
  19. RRDDATA=/mnt/www
  20. RRDOUTPUT=/mnt/www
  21. FORCEGRAPH=no
  22.  
  23. #-------------------------------------------------------------------
  24. # data definition: average load
  25. #-------------------------------------------------------------------
  26.  
  27. RRDcFILE[0]="load:60:Load graphs"
  28. RRDcDEF[0]='
  29. DS:l1:GAUGE:120:0:100
  30. DS:l5:GAUGE:120:0:100
  31. DS:l15:GAUGE:120:0:100
  32. RRA:AVERAGE:0.5:1:576
  33. RRA:AVERAGE:0.5:6:672
  34. RRA:AVERAGE:0.5:24:732
  35. RRA:AVERAGE:0.5:144:1460
  36. '
  37. RRDuSRC[0]="l1:l5:l15"
  38. RRDuVAL[0]='
  39. UT=$(head -n1 /proc/loadavg)
  40. L1=$(echo "$UT"|awk "{print \$1}")
  41. L5=$(echo "$UT"|awk "{print \$2}")
  42. L15=$(echo "$UT"|awk "{print \$3}")
  43. echo "${L1}:${L5}:${L15}"
  44. '
  45. RRDgUM[0]='proc/min'
  46. RRDgLIST[0]="0 1 2 3 4 5"
  47. RRDgDEF[0]=$(cat <<EOF
  48. 'DEF:ds1=\$RRD:l1:AVERAGE'
  49. 'DEF:ds2=\$RRD:l5:AVERAGE'
  50. 'DEF:ds3=\$RRD:l15:AVERAGE'
  51. 'CDEF:bo=ds1,UN,0,ds1,IF,0,GT,UNKN,INF,IF'
  52. 'AREA:bo#DDDDDD:'
  53. 'CDEF:bi=ds1,UN,0,ds1,IF,0,GT,INF,UNKN,IF'
  54. 'AREA:bi#FEFEED:'
  55. 'HRULE:1.0#44B5FF'
  56. 'AREA:ds3#FFEE00:Last 15 min'
  57.   'VDEF:max1=ds1,MAXIMUM'
  58.   'VDEF:min1=ds1,MINIMUM'
  59.   'VDEF:avg1=ds1,AVERAGE'
  60.   GPRINT:max1:"Max %6.2lf"
  61.   GPRINT:min1:"Min %6.2lf"
  62.   GPRINT:avg1:"Avg %6.2lf\n"
  63. 'LINE3:ds2#FFCC00:Last  5 min'
  64.   'VDEF:max2=ds2,MAXIMUM'
  65.   'VDEF:min2=ds2,MINIMUM'
  66.   'VDEF:avg2=ds2,AVERAGE'
  67.   GPRINT:max2:"Max %6.2lf"
  68.   GPRINT:min2:"Min %6.2lf"
  69.   GPRINT:avg2:"Avg %6.2lf\n"
  70. 'LINE1:ds1#FF0000:Last  1 min'
  71.   'VDEF:max3=ds3,MAXIMUM'
  72.   'VDEF:min3=ds3,MINIMUM'
  73.   'VDEF:avg3=ds3,AVERAGE'
  74.   GPRINT:max3:"Max %6.2lf"
  75.   GPRINT:min3:"Min %6.2lf"
  76.   GPRINT:avg3:"Avg %6.2lf\n"
  77. EOF
  78. )
  79.  
  80. RRDgGRAPH[0]='3600|load1|Load last hour|[ "$M" = 30 ]'
  81. RRDgGRAPH[1]='14400|load6|Load last 4H|[ "$M" = 30 ]'
  82. RRDgGRAPH[2]='86400|load24|Load last 24H|[ "$H" = 04 ] && [ "$M" = 30 ]|--x-grid HOUR:1:DAY:1:HOUR:1:0:%H'
  83. RRDgGRAPH[3]='604800|loadW|Load last week|[ "$H" = 04 ] && [ "$M" = 30 ]|--x-grid HOUR:4:DAY:1:DAY:1:0:"%a %d/%m"'
  84. RRDgGRAPH[4]='2678400|loadM|Load last month|[ "$H" = 04 ] && [ "$M" = 30 ]'
  85. RRDgGRAPH[5]='31536000|loadY|Load last year|[ "$H" = 04 ] && [ "$M" = 30 ]'
  86.  
  87. #-------------------------------------------------------------------
  88. # data definition: memory allocation
  89. #-------------------------------------------------------------------
  90.  
  91. RRDcFILE[1]="mem:60:Memory allocation"
  92. RRDcDEF[1]='
  93. DS:cached:GAUGE:120:0:1000000
  94. DS:buffer:GAUGE:120:0:1000000
  95. DS:free:GAUGE:120:0:1000000
  96. DS:total:GAUGE:120:0:1000000
  97. DS:swapt:GAUGE:120:0:1000000
  98. DS:swapf:GAUGE:120:0:1000000
  99. RRA:AVERAGE:0.5:1:576
  100. RRA:AVERAGE:0.5:6:672
  101. RRA:AVERAGE:0.5:24:732
  102. RRA:AVERAGE:0.5:144:1460
  103. '
  104. RRDuSRC[1]="cached:buffer:free:total:swapt:swapf"
  105. RRDuVAL[1]='
  106. C=$(grep ^Cached /proc/meminfo|awk "{print \$2}")
  107. B=$(grep ^Buffers /proc/meminfo|awk "{print \$2}")
  108. F=$(grep ^MemFree /proc/meminfo|awk "{print \$2}")
  109. T=$(grep ^MemTotal /proc/meminfo|awk "{print \$2}")
  110. ST=$(grep ^SwapTotal /proc/meminfo|awk "{print \$2}")
  111. SF=$(grep ^SwapFree /proc/meminfo|awk "{print \$2}")
  112. echo "${C}:${B}:${F}:${T}:${ST}:${SF}"
  113. '
  114. RRDgUM[1]='bytes'
  115. RRDgLIST[1]="6 7 8 9 10 11"
  116. RRDgDEF[1]=$(cat <<EOF
  117. 'DEF:dsC=\$RRD:cached:AVERAGE'
  118. 'DEF:dsB=\$RRD:buffer:AVERAGE'
  119. 'DEF:dsF=\$RRD:free:AVERAGE'
  120. 'DEF:dsT=\$RRD:total:AVERAGE'
  121. 'CDEF:bo=dsT,UN,0,dsT,IF,0,GT,UNKN,INF,IF'
  122. 'AREA:bo#DDDDDD:'
  123. 'CDEF:tot=dsT,1024,*'
  124. 'CDEF:fre=dsF,1024,*'
  125. 'CDEF:freP=fre,100,*,tot,/'
  126. 'CDEF:buf=dsB,1024,*'
  127. 'CDEF:bufP=buf,100,*,tot,/'
  128. 'CDEF:cac=dsC,1024,*'
  129. 'CDEF:cacP=cac,100,*,tot,/'
  130. 'CDEF:use=dsT,dsF,dsC,+,dsB,+,-,1024,*'
  131. 'CDEF:useP=use,100,*,tot,/'
  132. 'CDEF:l=use,1,1,IF'
  133. 'AREA:use#CC3300:User   '
  134. 'LINE2:l#AC1300::STACK'
  135.   'VDEF:maxU=use,MAXIMUM'
  136.   'VDEF:minU=use,MINIMUM'
  137.   'VDEF:avgU=use,AVERAGE'
  138.   'VDEF:curU=use,LAST'
  139.   'VDEF:procU=useP,LAST'
  140.   GPRINT:curU:"Last %6.2lf %s"
  141.   GPRINT:procU:"%3.0lf%%"
  142.   GPRINT:avgU:"Avg %6.2lf %s"
  143.   GPRINT:maxU:"Max %6.2lf %s"
  144.   GPRINT:minU:"Min %6.2lf %s\n"
  145. 'AREA:cac#FF9900:Cached :STACK'
  146. 'LINE2:l#DF7900::STACK'
  147.   'VDEF:maxC=cac,MAXIMUM'
  148.   'VDEF:minC=cac,MINIMUM'
  149.   'VDEF:avgC=cac,AVERAGE'
  150.   'VDEF:curC=cac,LAST'
  151.   'VDEF:procC=cacP,LAST'
  152.   GPRINT:curC:"Last %6.2lf %s"
  153.   GPRINT:procC:"%3.0lf%%"
  154.   GPRINT:avgC:"Avg %6.2lf %s"
  155.   GPRINT:maxC:"Max %6.2lf %s"
  156.   GPRINT:minC:"Min %6.2lf %s\n"
  157. 'AREA:buf#FFCC00:Buffers:STACK'
  158. 'LINE2:l#DFAC00::STACK'
  159.   'VDEF:maxB=buf,MAXIMUM'
  160.   'VDEF:minB=buf,MINIMUM'
  161.   'VDEF:avgB=buf,AVERAGE'
  162.   'VDEF:curB=buf,LAST'
  163.   'VDEF:procB=bufP,LAST'
  164.   GPRINT:curB:"Last %6.2lf %s"
  165.   GPRINT:procB:"%3.0lf%%"
  166.   GPRINT:avgB:"Avg %6.2lf %s"
  167.   GPRINT:maxB:"Max %6.2lf %s"
  168.   GPRINT:minB:"Min %6.2lf %s\n"
  169. 'AREA:fre#FFFFCC:Unused :STACK'
  170.   'VDEF:maxF=fre,MAXIMUM'
  171.   'VDEF:minF=fre,MINIMUM'
  172.   'VDEF:avgF=fre,AVERAGE'
  173.   'VDEF:curF=fre,LAST'
  174.   'VDEF:procF=freP,LAST'
  175.   GPRINT:curF:"Last %6.2lf %s"
  176.   GPRINT:procF:"%3.0lf%%"
  177.   GPRINT:avgF:"Avg %6.2lf %s"
  178.   GPRINT:maxF:"Max %6.2lf %s"
  179.   GPRINT:minF:"Min %6.2lf %s\n"
  180. EOF
  181. )
  182.  
  183. RRDgGRAPH[6]='3600|mem1|RAM last hour|[ "$M" = 30 ]|-l 0 -r'
  184. RRDgGRAPH[7]='14400|mem6|RAM last 4H|[ "$M" = 30 ]|-l 0 -r'
  185. RRDgGRAPH[8]='86400|mem24|RAM last 24H|[ "$H" = 04 ] && [ "$M" = 30 ]|-l 0 -r --x-grid HOUR:1:DAY:1:HOUR:1:0:%H'
  186. RRDgGRAPH[9]='604800|memW|RAM last week|[ "$H" = 04 ] && [ "$M" = 30 ]|-l 0 -r --x-grid HOUR:4:DAY:1:DAY:1:0:"%a %d/%m"'
  187. RRDgGRAPH[10]='2678400|memM|RAM last month|[ "$H" = 04 ] && [ "$M" = 30 ]|-l 0 -r'
  188. RRDgGRAPH[11]='31536000|memY|RAM last year|[ "$H" = 04 ] && [ "$M" = 30 ]|-l 0 -r'
  189.  
  190. #-------------------------------------------------------------------
  191. # data definition: CPU temp and fan
  192. #-------------------------------------------------------------------
  193.  
  194. RRDcFILE[2]="temp:60:CPU Temperature and Fan RPM"
  195. RRDcDEF[2]='
  196. DS:cpu:GAUGE:120:0:100
  197. DS:mobo:GAUGE:120:0:100
  198. DS:sys:GAUGE:120:0:100
  199. DS:fanc:GAUGE:120:0:4000
  200. DS:fanf:GAUGE:120:0:4000
  201. DS:hdsys:GAUGE:120:0:100
  202. DS:hdr1:GAUGE:120:0:100
  203. DS:hdr2:GAUGE:120:0:100
  204. RRA:AVERAGE:0.5:1:576
  205. RRA:AVERAGE:0.5:6:672
  206. RRA:AVERAGE:0.5:24:732
  207. RRA:AVERAGE:0.5:144:1460
  208. '
  209. RRDuSRC[2]="cpu:mobo:sys:fanc:fanf:hdsys:hdr1:hdr2"
  210. RRDuVAL[2]='
  211. CPU=$(sensors|grep ^MoBo:|awk "{print \$2}"|sed "s/[^0-9.]//g")
  212. MOBO=$(sensors|grep ^CPU:|awk "{print \$2}"|sed "s/[^0-9.]//g")
  213. SYS=$(sensors|grep ^System:|awk "{print \$2}"|sed "s/[^0-9.]//g")
  214. FANC=$(sensors|grep "CPU Fan:"|awk "{print \$3}")
  215. FANF=$(sensors|grep "Front Fan:"|awk "{print \$3}"|cut -d: -f2)
  216. HDSYS=$(netcat localhost 7634|cut -d"|" -f4)
  217. HDR1=$(netcat localhost 7634|cut -d"|" -f9)
  218. HDR2=$(netcat localhost 7634|cut -d"|" -f14)
  219. echo "${CPU}:${MOBO}:${SYS}:${FANC}:${FANF}:${HDSYS}:${HDR1}:${HDR2}"
  220. '
  221. RRDgUM[2]='Celsius and RPM'
  222. RRDgLIST[2]="12 13 14 15 16 17"
  223. #RRDgLIST[2]="12"
  224. RRDgDEF[2]=$(cat <<EOF
  225. 'DEF:c=\$RRD:cpu:AVERAGE'
  226. 'DEF:m=\$RRD:mobo:AVERAGE'
  227. 'DEF:s=\$RRD:sys:AVERAGE'
  228. 'DEF:fc=\$RRD:fanc:AVERAGE'
  229. 'DEF:ff=\$RRD:fanf:AVERAGE'
  230. 'DEF:hs=\$RRD:hdsys:AVERAGE'
  231. 'DEF:hr1=\$RRD:hdr1:AVERAGE'
  232. 'DEF:hr2=\$RRD:hdr2:AVERAGE'
  233. 'CDEF:bo=c,UN,0,c,IF,0,GT,UNKN,INF,IF'
  234. 'AREA:bo#DDDDDD:'
  235. 'AREA:m#CBFE66:CPU        '
  236.   'VDEF:maxM=m,MAXIMUM'
  237.   'VDEF:minM=m,MINIMUM'
  238.   'VDEF:avgM=m,AVERAGE'
  239.   GPRINT:minM:"Min %2.1lf"
  240.   GPRINT:maxM:"Max %2.1lf"
  241.   GPRINT:avgM:"Avg %2.1lf Celsius\n"
  242. 'LINE1:m#ABDE46:'
  243. 'AREA:c#BAE366:Chipset    '
  244.   'VDEF:maxC=c,MAXIMUM'
  245.   'VDEF:minC=c,MINIMUM'
  246.   'VDEF:avgC=c,AVERAGE'
  247.   GPRINT:minC:"Min %2.1lf"
  248.   GPRINT:maxC:"Max %2.1lf"
  249.   GPRINT:avgC:"Avg %2.1lf Celsius\n"
  250. 'LINE1:c#9AC346:'
  251. 'AREA:s#00CB33:Case       '
  252.   'VDEF:maxS=s,MAXIMUM'
  253.   'VDEF:minS=s,MINIMUM'
  254.   'VDEF:avgS=s,AVERAGE'
  255.   GPRINT:minS:"Min %2.1lf"
  256.   GPRINT:maxS:"Max %2.1lf"
  257.   GPRINT:avgS:"Avg %2.1lf Celsius\n"
  258. 'LINE1:s#00AB13:'
  259. 'CDEF:f1=fc,0.021,*'
  260. 'LINE2:f1#6B7FD3:CPU fan    '
  261.   'VDEF:maxF=fc,MAXIMUM'
  262.   'VDEF:minF=fc,MINIMUM'
  263.   'VDEF:avgF=fc,AVERAGE'
  264.   GPRINT:minF:"Min %4.0lf"
  265.   GPRINT:maxF:"Max %4.0lf"
  266.   GPRINT:avgF:"Avg %4.0lf RPM\n"
  267. 'CDEF:f2=ff,0.028,*'
  268. 'LINE2:f2#FF00A3:Front fan  '
  269.   'VDEF:maxFF=ff,MAXIMUM'
  270.   'VDEF:minFF=ff,MINIMUM'
  271.   'VDEF:avgFF=ff,AVERAGE'
  272.   GPRINT:minFF:"Min %4.0lf"
  273.   GPRINT:maxFF:"Max %4.0lf"
  274.   GPRINT:avgFF:"Avg %4.0lf RPM\n"
  275. 'LINE4:hs#FF0000:HDD system '
  276.   'VDEF:maxHS=hs,MAXIMUM'
  277.   'VDEF:minHS=hs,MINIMUM'
  278.   'VDEF:avgHS=hs,AVERAGE'
  279.   GPRINT:minHS:"Min %2.1lf"
  280.   GPRINT:maxHS:"Max %2.1lf"
  281.   GPRINT:avgHS:"Avg %2.1lf Celsius\n"
  282. 'LINE4:hr2#FFBB00:HDD RAID1\:0'
  283.   'VDEF:maxH2=hr2,MAXIMUM'
  284.   'VDEF:minH2=hr2,MINIMUM'
  285.   'VDEF:avgH2=hr2,AVERAGE'
  286.   GPRINT:minH2:"Min %2.1lf"
  287.   GPRINT:maxH2:"Max %2.1lf"
  288.   GPRINT:avgH2:"Avg %2.1lf Celsius\n"
  289. 'LINE4:hr1#FF8700:HDD RAID1\:1'
  290.   'VDEF:maxH1=hr1,MAXIMUM'
  291.   'VDEF:minH1=hr1,MINIMUM'
  292.   'VDEF:avgH1=hr1,AVERAGE'
  293.   GPRINT:minH1:"Min %2.1lf"
  294.   GPRINT:maxH1:"Max %2.1lf"
  295.   GPRINT:avgH1:"Avg %2.1lf Celsius\n"
  296. EOF
  297. )
  298.  
  299. RRDgGRAPH[12]='3600|temp1|PC temperatures and fans (last hour)|[ "$M" = 30 ]|-l 20 -r'
  300. RRDgGRAPH[13]='14400|temp6|PC temperatures and fans (last 4H)|[ "$M" = 30 ]|-l 20 -r'
  301. RRDgGRAPH[14]='86400|temp24|PC temperatures and fans (last 24H)|[ "$M" = 30 ]|-l 20 -r'
  302. RRDgGRAPH[15]='604800|tempW|PC temperatures and fans (last week)|[ "$M" = 30 ]|-l 20 -r'
  303. RRDgGRAPH[16]='2678400|tempM|PC temperatures and fans (last month)|[ "$M" = 30 ]|-l 20 -r'
  304. RRDgGRAPH[17]='31536000|tempY|PC temperatures and fans (last year)|[ "$M" = 30 ]|-l 20 -r'
  305.  
  306. #-------------------------------------------------------------------
  307. # data definition: cpu usage
  308. #-------------------------------------------------------------------
  309.  
  310. RRDcFILE[3]="cpu:60:CPU Usage"
  311. RRDcDEF[3]='
  312. DS:user:DERIVE:120:0:U
  313. DS:nice:DERIVE:120:0:U
  314. DS:sys:DERIVE:120:0:U
  315. DS:idle:DERIVE:120:0:U
  316. RRA:AVERAGE:0.5:1:576
  317. RRA:AVERAGE:0.5:6:672
  318. RRA:AVERAGE:0.5:24:732
  319. RRA:AVERAGE:0.5:144:1460
  320. '
  321. RRDuSRC[3]="user:nice:sys:idle"
  322. RRDuVAL[3]='
  323. cat /proc/stat|head -1|sed "s/^cpu\ \+\([0-9]*\)\ \([0-9]*\)\ \([0-9]*\)\ \([0-9]*\).*/\1:\2:\3:\4/"
  324. '
  325. RRDgUM[3]='jiffies'
  326. RRDgLIST[3]="18 19 20 21 22 23"
  327. RRDgDEF[3]=$(cat <<EOF
  328. 'DEF:uj=\$RRD:user:AVERAGE'
  329. 'DEF:nj=\$RRD:nice:AVERAGE'
  330. 'DEF:sj=\$RRD:sys:AVERAGE'
  331. 'DEF:ij=\$RRD:idle:AVERAGE'
  332. 'CDEF:l=uj,0.1,0.1,IF'
  333. 'CDEF:bo=uj,UN,0,uj,IF,0,GT,UNKN,INF,IF'
  334. 'AREA:bo#DDDDDD:'
  335. 'CDEF:tj=uj,nj,+,sj,+,ij,+'
  336. 'CDEF:usr=100,uj,*,tj,/'
  337. 'CDEF:nic=100,nj,*,tj,/'
  338. 'CDEF:sys=100,sj,*,tj,/'
  339. 'CDEF:idl=100,ij,*,tj,/'
  340. 'CDEF:tot=100,tj,*,tj,/'
  341. 'AREA:nic#0040A2:Nice  '
  342.   'VDEF:maxN=nic,MAXIMUM'
  343.   'VDEF:minN=nic,MINIMUM'
  344.   'VDEF:avgN=nic,AVERAGE'
  345.   GPRINT:maxN:"Max %6.2lf%%"
  346.   GPRINT:minN:"Min %6.2lf%%"
  347.   GPRINT:avgN:"Avg %6.2lf%%\n"
  348. 'AREA:sys#90C5CC:System:STACK'
  349. 'LINE2:l#70A5AC::STACK'
  350.   'VDEF:maxS=sys,MAXIMUM'
  351.   'VDEF:minS=sys,MINIMUM'
  352.   'VDEF:avgS=sys,AVERAGE'
  353.   GPRINT:maxS:"Max %6.2lf%%"
  354.   GPRINT:minS:"Min %6.2lf%%"
  355.   GPRINT:avgS:"Avg %6.2lf%%\n"
  356. 'AREA:usr#B0E5EC:User  :STACK'
  357. 'LINE2:l#90C5CC::STACK'
  358.   'VDEF:maxU=usr,MAXIMUM'
  359.   'VDEF:minU=usr,MINIMUM'
  360.   'VDEF:avgU=usr,AVERAGE'
  361.   GPRINT:maxU:"Max %6.2lf%%"
  362.   GPRINT:minU:"Min %6.2lf%%"
  363.   GPRINT:avgU:"Avg %6.2lf%%\n"
  364. 'AREA:idl#EEFFFF:Idle  :STACK'
  365.   'VDEF:maxI=idl,MAXIMUM'
  366.   'VDEF:minI=idl,MINIMUM'
  367.   'VDEF:avgI=idl,AVERAGE'
  368.   GPRINT:maxI:"Max %6.2lf%%"
  369.   GPRINT:minI:"Min %6.2lf%%"
  370.   GPRINT:avgI:"Avg %6.2lf%%\n"
  371. EOF
  372. )
  373.  
  374. RRDgGRAPH[18]='3600|cpu1|CPU Usage (1H)|[ "$M" = 30 ]|-l 0 -r -u 99.99'
  375. RRDgGRAPH[19]='14400|cpu6|CPU Usage (4H)|[ "$M" = 30 ]|-r -l 0 -u 99.99'
  376. RRDgGRAPH[20]='86400|cpu24|CPU Usage (24H)|[ "$H" = 04 ] && [ "$M" = 30 ]|-r -l 0 -u 99.99 --x-grid HOUR:1:DAY:1:HOUR:1:0:%H'
  377. RRDgGRAPH[21]='604800|cpuW|CPU Usage (last week)|[ "$H" = 04 ] && [ "$M" = 30 ]|-r -l 0 -u 99.99 --x-grid HOUR:4:DAY:1:DAY:1:0:"%a %d/%m"'
  378. RRDgGRAPH[22]='2678400|cpuM|CPU Usage (last month)|[ "$H" = 04 ] && [ "$M" = 30 ]|-r -l 0 -u 99.99'
  379. RRDgGRAPH[23]='31536000|cpuY|CPU Usage (last year)|[ "$H" = 04 ] && [ "$M" = 30 ]|-r -l 0 -u 99.99'
  380.  
  381. #-------------------------------------------------------------------
  382. # network stats
  383. #-------------------------------------------------------------------
  384.  
  385. RRDcFILE[4]="wan:60:WAN Traffic"
  386. RRDcDEF[4]='
  387. DS:in:DERIVE:600:0:12500000
  388. DS:out:DERIVE:600:0:12500000
  389. RRA:AVERAGE:0.5:1:576
  390. RRA:AVERAGE:0.5:6:672
  391. RRA:AVERAGE:0.5:24:732
  392. RRA:AVERAGE:0.5:144:1460
  393. '
  394. RRDuSRC[4]="in:out"
  395. RRDuVAL[4]='
  396. IF="vlan1"
  397. IN=$(grep "${IF}" /proc/net/dev|awk -F ":" "{print \$2}"|awk "{print \$1}")
  398. OUT=$(grep "${IF}" /proc/net/dev|awk -F ":" "{print \$2}"|awk "{print \$9}")
  399. echo "${IN}:${OUT}"
  400. '
  401. RRDgUM[4]='bytes/s'
  402. RRDgLIST[4]="24 25 26 27 28 29"
  403. RRDgDEF[4]=$(cat <<EOF
  404. 'DEF:ds1=\$RRD:in:AVERAGE'
  405. 'DEF:ds2=\$RRD:out:AVERAGE'
  406.   'VDEF:max1=ds1,MAXIMUM'
  407. 'CDEF:ui=ds1,UN,0,ds1,IF,0,GT,UNKN,NEGINF,IF'
  408. 'CDEF:uo=0,ds1,UN,0,ds1,IF,0,GT,max1,50,/,UNKN,IF,-'
  409. 'CDEF:bi=ds1,UN,0,ds1,IF,0,GT,INF,UNKN,IF'
  410. 'CDEF:bo=ds1,UN,0,ds1,IF,0,GT,UNKN,INF,IF'
  411. 'AREA:bi#EDFEED:'
  412. 'AREA:ds1#00B5E3:Incoming'
  413. 'LINE1:ds1#0085B3:'
  414.   'VDEF:min1=ds1,MINIMUM'
  415.   'VDEF:avg1=ds1,AVERAGE'
  416.   'VDEF:tot1=ds1,TOTAL'
  417.   GPRINT:max1:"Max %6.2lf %s"
  418.   GPRINT:min1:"Min %6.2lf %s"
  419.   GPRINT:avg1:"Avg %6.2lf %s"
  420.   GPRINT:tot1:"Sum %6.2lf %s"
  421. 'AREA:ui#FF6666:Offline\n'
  422. 'LINE2:ds2#E32D00:Outgoing'
  423.   'VDEF:max2=ds2,MAXIMUM'
  424.   'VDEF:min2=ds2,MINIMUM'
  425.   'VDEF:avg2=ds2,AVERAGE'
  426.   'VDEF:tot2=ds2,TOTAL'
  427.   GPRINT:max2:"Max %6.2lf %s"
  428.   GPRINT:min2:"Min %6.2lf %s"
  429.   GPRINT:avg2:"Avg %6.2lf %s"
  430.   GPRINT:tot2:"Sum %6.2lf %s"
  431. 'AREA:bo#FEEDED:'
  432. 'AREA:uo#00FE00:Online'
  433. 'HRULE:0#000000'
  434. EOF
  435. )  
  436.    
  437. RRDgGRAPH[24]='3600|wan1|WAN Traffic (1H)|[ "$M" = 30 ]|-r'
  438. RRDgGRAPH[25]='14400|wan6|WAN Traffic (4H)|[ "$M" = 30 ]|-r'
  439. RRDgGRAPH[26]='86400|wan24|WAN Traffic (24H)|[ "$M" -ge 30 ] && [ "$M" -le 45 ]|-r --x-grid HOUR:1:DAY:1:HOUR:1:0:%H'
  440. RRDgGRAPH[27]='604800|wanW|WAN Traffic (last week)|[ "$M" -ge 30 ] && [ "$M" -le 45 ]|-r --x-grid HOUR:4:DAY:1:DAY:1:0:"%a %d/%m"'
  441. RRDgGRAPH[28]='2678400|wanM|WAN Traffic (last month)|[ "$H" = 04 ] && [ "$M" -ge 30 ] && [ "$M" -le 45 ]|-r '
  442. RRDgGRAPH[29]='31536000|wanY|WAN Traffic (last year)|[ "$H" = 04 ] && [ "$M" -ge 30 ] && [ "$M" -le 45 ]|-r '
  443.  
  444. #-------------------------------------------------------------------
  445. # disk space
  446. #-------------------------------------------------------------------
  447.  
  448. RRDcFILE[5]="hdd:60:Disk space"
  449. RRDcDEF[5]='
  450. DS:optprosto:GAUGE:600:0:U 
  451. DS:optzasede:GAUGE:600:0:U
  452. DS:mntprosto:GAUGE:600:0:U
  453. DS:mntzasede:GAUGE:600:0:U
  454. RRA:AVERAGE:0.5:1:576
  455. RRA:AVERAGE:0.5:6:672
  456. RRA:AVERAGE:0.5:24:732
  457. RRA:AVERAGE:0.5:144:1460
  458. '
  459. RRDuSRC[5]="optprosto:optzasede:mntprosto:mntzasede"
  460. RRDuVAL[5]='
  461. SP=$(/opt/bin/coreutils-df "-B1")
  462. echo -n $(echo "$SP"|grep opt|awk "{print \$3\":\"\$2}"):
  463. echo -n $(echo "$SP"|grep mnt|awk "{print \$3\":\"\$2}")
  464. echo
  465. '
  466. RRDgUM[5]='space (bytes)'
  467. RRDgLIST[5]="30 31 32 33 34 35"
  468. RRDgDEF[5]=$(cat <<EOF
  469. 'DEF:optzasede=\$RRD:optzasede:AVERAGE'
  470. 'DEF:optprosto=\$RRD:optprosto:AVERAGE'
  471. 'DEF:mntzasede=\$RRD:mntzasede:AVERAGE'
  472. 'DEF:mntprosto=\$RRD:mntprosto:AVERAGE'
  473. 'CDEF:bo=mntzasede,UN,0,mntzasede,IF,0,GT,UNKN,INF,IF'
  474. 'AREA:bo#DDDDDD:'
  475. 'AREA:mntzasede#CC0033:/mnt:'
  476.   'CDEF:root=mntzasede,mntprosto,+'
  477.   'VDEF:sumr=root,LAST'
  478.   GPRINT:sumr:"Total %6.2lf %sB"
  479.   'VDEF:lasr=mntzasede,LAST'
  480.   GPRINT:lasr:"Used %6.2lf %sB"
  481.   'CDEF:rootPu=mntzasede,100,*,root,/'
  482.   'VDEF:procr=rootPu,LAST'
  483.   GPRINT:procr:"%6.2lf%%"
  484. 'AREA:mntprosto#EC2053:Free:STACK'
  485.   'VDEF:lasra=mntprosto,LAST'
  486.   GPRINT:lasra:"%6.2lf %sB"
  487.   'CDEF:rootPa=100,rootPu,-'
  488.   'VDEF:procar=rootPa,LAST'
  489.   GPRINT:procar:"%6.2lf%%\n"
  490. 'AREA:optzasede#33CC00:/opt:STACK'
  491.   'CDEF:home=optzasede,optprosto,+'
  492.   'VDEF:sumh=home,LAST'
  493.   GPRINT:sumh:"Total %6.2lf %sB"
  494.   'VDEF:lash=optzasede,LAST'
  495.   GPRINT:lash:"Used %6.2lf %sB"
  496.   'CDEF:homePu=optzasede,100,*,home,/'
  497.   'VDEF:proch=homePu,LAST'
  498.   GPRINT:proch:"%6.2lf%%"
  499. 'AREA:optprosto#53EC20:Free:STACK'
  500.   'VDEF:lasha=optprosto,LAST'
  501.   GPRINT:lasha:"%6.2lf %sB"
  502.   'CDEF:homePa=100,homePu,-'
  503.   'VDEF:procah=homePa,LAST'
  504.   GPRINT:procah:"%6.2lf%%\n"
  505. EOF
  506. )  
  507.  
  508. RRDgGRAPH[30]='3600|hdd1|Disk space (1H)|[ "$M" = 30 ]|-r -l 0'
  509. RRDgGRAPH[31]='14400|hdd6|Disk space (4H)|[ "$M" = 30 ]|-r -l 0'
  510. RRDgGRAPH[32]='86400|hdd24|Disk space (24H)|[ "$M" -ge 30 ] && [ "$M" -le 45 ]|-r -l 0 --x-grid HOUR:1:DAY:1:HOUR:1:0:%H'
  511. RRDgGRAPH[33]='604800|hddW|Disk space (last week)|[ "$M" -ge 30 ] && [ "$M" -le 45 ]|-r -l 0 --x-grid HOUR:4:DAY:1:DAY:1:0:"%a %d/%m"'
  512. RRDgGRAPH[34]='2678400|hddM|Disk space (last month)|[ "$H" = 04 ] && [ "$M" -ge 30 ] && [ "$M" -le 45 ]|-r -l 0'
  513. RRDgGRAPH[35]='31536000|hddY|Disk space (last year)|[ "$H" = 04 ] && [ "$M" -ge 30 ] && [ "$M" -le 45 ]|-r -l 0'
  514.  
  515. #-------------------------------------------------------------------
  516. # network stats
  517. #-------------------------------------------------------------------
  518.  
  519. RRDcFILE[6]="wlan:60:WLAN Traffic"
  520. RRDcDEF[6]='
  521. DS:in:DERIVE:600:0:U
  522. DS:out:DERIVE:600:0:U
  523. RRA:AVERAGE:0.5:1:576
  524. RRA:AVERAGE:0.5:6:672
  525. RRA:AVERAGE:0.5:24:732
  526. RRA:AVERAGE:0.5:144:1460
  527. '
  528. RRDuSRC[6]="in:out"
  529. RRDuVAL[6]='
  530. IF="eth1"
  531. IN=$(grep "${IF}" /proc/net/dev|awk -F ":" "{print \$2}"|awk "{print \$1}")
  532. OUT=$(grep "${IF}" /proc/net/dev|awk -F ":" "{print \$2}"|awk "{print \$9}")
  533. echo "${IN}:${OUT}"
  534. '
  535. RRDgUM[6]='bytes/s'
  536. RRDgLIST[6]="36 37 38 39 40 41"
  537. RRDgDEF[6]=$(cat <<EOF
  538. 'DEF:ds1=\$RRD:in:AVERAGE'
  539. 'DEF:ds2=\$RRD:out:AVERAGE'
  540.   'VDEF:max1=ds1,MAXIMUM'
  541. 'CDEF:ui=ds1,UN,0,ds1,IF,0,GT,UNKN,NEGINF,IF'
  542. 'CDEF:uo=0,ds1,UN,0,ds1,IF,0,GT,max1,50,/,UNKN,IF,-'
  543. 'CDEF:bi=ds1,UN,0,ds1,IF,0,GT,INF,UNKN,IF'
  544. 'CDEF:bo=ds1,UN,0,ds1,IF,0,GT,UNKN,INF,IF'
  545. 'AREA:bi#EDFEED:'
  546. 'AREA:ds1#00B5E3:Incoming'
  547. 'LINE1:ds1#0085B3:'
  548.   'VDEF:min1=ds1,MINIMUM'
  549.   'VDEF:avg1=ds1,AVERAGE'
  550.   'VDEF:tot1=ds1,TOTAL'
  551.   GPRINT:max1:"Max %6.2lf %s"
  552.   GPRINT:min1:"Min %6.2lf %s"
  553.   GPRINT:avg1:"Avg %6.2lf %s"
  554.   GPRINT:tot1:"Sum %6.2lf %s"
  555. 'AREA:ui#FF6666:Offline\n'
  556. 'LINE2:ds2#E32D00:Outgoing'
  557.   'VDEF:max2=ds2,MAXIMUM'
  558.   'VDEF:min2=ds2,MINIMUM'
  559.   'VDEF:avg2=ds2,AVERAGE'
  560.   'VDEF:tot2=ds2,TOTAL'
  561.   GPRINT:max2:"Max %6.2lf %s"
  562.   GPRINT:min2:"Min %6.2lf %s"
  563.   GPRINT:avg2:"Avg %6.2lf %s"
  564.   GPRINT:tot2:"Sum %6.2lf %s"
  565. 'AREA:bo#FEEDED:'
  566. 'AREA:uo#00FE00:Online'
  567. 'HRULE:0#000000'
  568. EOF
  569. )  
  570.    
  571. RRDgGRAPH[36]='3600|wlan1|WLAN Traffic (1H)|[ "$M" = 30 ]|-r'
  572. RRDgGRAPH[37]='14400|wlan6|WLAN Traffic (4H)|[ "$M" = 30 ]|-r'
  573. RRDgGRAPH[38]='86400|wlan24|WLAN Traffic (24H)|[ "$M" -ge 30 ] && [ "$M" -le 45 ]|-r --x-grid HOUR:1:DAY:1:HOUR:1:0:%H'
  574. RRDgGRAPH[39]='604800|wlanW|WLAN Traffic (last week)|[ "$M" -ge 30 ] && [ "$M" -le 45 ]|-r --x-grid HOUR:4:DAY:1:DAY:1:0:"%a %d/%m"'
  575. RRDgGRAPH[40]='2678400|wlanM|WLAN Traffic (last month)|[ "$H" = 04 ] && [ "$M" -ge 30 ] && [ "$M" -le 45 ]|-r '
  576. RRDgGRAPH[41]='31536000|wlanY|WLAN Traffic (last year)|[ "$H" = 04 ] && [ "$M" -ge 30 ] && [ "$M" -le 45 ]|-r '
  577.  
  578. #-------------------------------------------------------------------
  579. # swap allocation
  580. #-------------------------------------------------------------------
  581.  
  582. RRDcFILE[7]="mem:60:Swap allocation"
  583. RRDgUM[7]='bytes'
  584. RRDgLIST[7]="42 43 44 45 46 47"
  585. RRDgDEF[7]=$(cat <<EOF
  586. 'DEF:sT=\$RRD:swapt:AVERAGE'
  587. 'DEF:sF=\$RRD:swapf:AVERAGE'
  588. 'CDEF:sU=sT,sF,-'
  589. 'CDEF:bo=sT,UN,0,sT,IF,0,GT,UNKN,561936,IF'
  590. 'AREA:bo#FFCCCC:'
  591. 'AREA:sU#9999FF:Used'
  592. 'AREA:sF#FFFF99:Free:STACK'
  593. 'LINE:sU#7777DD:'
  594. 'HRULE:31832#FF0000'
  595. 'HRULE:561936#FF0000'
  596. EOF
  597. )
  598.  
  599. RRDgGRAPH[42]='3600|swap1|Swap last hour|[ "$M" = 30 ]|-l 0 -r'
  600. RRDgGRAPH[43]='14400|swap6|Swap last 4H|[ "$M" = 30 ]|-l 0 -r'
  601. RRDgGRAPH[44]='86400|swap24|Swap last 24H|[ "$H" = 04 ] && [ "$M" = 30 ]|-l 0 -r --x-grid HOUR:1:DAY:1:HOUR:1:0:%H'
  602. RRDgGRAPH[45]='604800|swapW|Swap last week|[ "$H" = 04 ] && [ "$M" = 30 ]|-l 0 -r --x-grid HOUR:4:DAY:1:DAY:1:0:"%a %d/%m"'
  603. RRDgGRAPH[46]='2678400|swapM|Swap last month|[ "$H" = 04 ] && [ "$M" = 30 ]|-l 0 -r'
  604. RRDgGRAPH[47]='31536000|swapY|Swap last year|[ "$H" = 04 ] && [ "$M" = 30 ]|-l 0 -r'
  605.  
  606.  
  607.  
  608.  
  609. ####################################################################
  610. # STOP MODIFICATIONS HERE, UNLESS YOU REALLY KNOW WHAT YOU'RE DOING
  611. ####################################################################
  612.  
  613. #-------------------------------------------------------------------
  614. # functions
  615. #-------------------------------------------------------------------
  616.  
  617. #1=rrdfile 2=step 3=definition
  618. CreateRRD()
  619. {  
  620.     "$RRDTOOL" create "$1" --step "$2" $3
  621. }
  622.  
  623. #1=file, 2=data sources, 3=values
  624. UpdateRRD()
  625. {
  626.     "$RRDUPDATE" "$1" -t "$2" "N:${3}"
  627. }
  628.  
  629. #1=imgfile, 2=secs to go back, 3=um text, 4=title text,
  630. #5=rrdfile, 6=definition, 7=extra params
  631. CreateGraph()
  632. {
  633.   RRD="$5"
  634.     DEF=$(echo "${6} "|sed 's/"/\\"/g'|sed '/[^ ]$/s/$/ \\/')
  635.   eval "DEF=\"$DEF\""
  636.     eval "\"$RRDTOOL\" graph \"$1\" $7 -M -a PNG -s \"-${2}\" -e -20 -w 550 -h 240 -v \"$3\" -t \"$4\" $DEF"
  637. }
  638.  
  639. #-------------------------------------------------------------------
  640. # main code
  641. #-------------------------------------------------------------------
  642.  
  643. # TODO: examine parameters and output help if any mistake
  644.  
  645. # grab command
  646. COMMAND="$1"
  647. CRON_GRAPH_TIME="$2"
  648. shift
  649.  
  650. # prepare main HTML index file
  651. [ "$COMMAND" = create ] && {
  652.     HTMLINDEX="${RRDOUTPUT}/index.html"
  653.     [ -f "$HTMLINDEX" ] || {
  654.         echo "<head><title>RRDStorm</title>
  655.             <style>body{background:white;color:black}</style></head>
  656.             <body><h1>RRDStorm</h1><ul>" > "$HTMLINDEX"
  657.         MAKEINDEX=yes
  658.     }
  659. }
  660. # cycle numbers
  661. for N in "$@"; do
  662.     # does this N exist?
  663.     [ -z "${RRDcFILE[$N]}" ] && continue
  664.     # extract common data
  665.     FILEBASE=$(echo "${RRDcFILE[$N]}"|awk -F: '{print $1}')
  666.     RRDFILE="${RRDDATA}/${FILEBASE}.rrd"
  667.     # honor command
  668.     case "$COMMAND" in
  669.         create)
  670.             # extract base data
  671.             HTMLFILE="${RRDOUTPUT}/${FILEBASE}.html"
  672.             STEP=$(echo "${RRDcFILE[$N]}"|awk -F: '{print $2}')
  673.             HTITLE=$(echo "${RRDcFILE[$N]}"|awk -F: '{print $3}')
  674.             # check RRD archive
  675.             [ -d "$RRDDATA" ] || mkdir -p "$RRDDATA"
  676.             [ -f "$RRDFILE" ] || CreateRRD  "$RRDFILE" "$STEP" "${RRDcDEF[$N]}"
  677.             # check individual HTML file
  678.             [ -d "$RRDOUTPUT" ] || mkdir -p "$RRDOUTPUT"
  679.             [ -f "$HTMLFILE" ] || {
  680.                 echo "<head><title>${HTITLE}</title>
  681.                     <style>body{background:white;color:black}</style></head>
  682.                     <body><h1>${HTITLE}</h1><center>" > "$HTMLFILE"
  683.                 for P in ${RRDgLIST[$N]}; do
  684.                     [ -z "${RRDgGRAPH[$P]}" ] && continue
  685.                     IMGBASE=$(echo "${RRDgGRAPH[$P]}"|cut -d'|' -f2)
  686.                     echo "<img src=\"${IMGBASE}.png\"><br>" >> "$HTMLFILE"
  687.                 done
  688.                 echo "</center><p>RRDStorm v${VERSION}/${DATE}</p></body>" >> "$HTMLFILE"
  689.             }
  690.             # update the main HTML index
  691.             [ ! -z "$MAKEINDEX" ] && {
  692.                 echo "<li><a href=\"${FILEBASE}.html\">${HTITLE}</a>" >> "$HTMLINDEX"
  693.             }
  694.         ;;
  695.         update)
  696.             VAL=$(eval "${RRDuVAL[$N]}")
  697.             echo "Updating ($N) $RRDFILE with $VAL .."
  698.             UpdateRRD "$RRDFILE" "${RRDuSRC[$N]}" "$VAL"
  699.         ;;
  700.         graph)
  701.             # grab hour and minute
  702.             M=$(date "+%M")
  703.             H=$(date "+%H")
  704.             # do graphs
  705.             for P in ${RRDgLIST[$N]}; do
  706.                 [ -z "${RRDgGRAPH[$P]}" ] && continue
  707.                 BACK=$(echo "${RRDgGRAPH[$P]}"|cut -d'|' -f1)
  708.                 IMGBASE=$(echo "${RRDgGRAPH[$P]}"|cut -d'|' -f2)
  709.                 TITLE=$(echo "${RRDgGRAPH[$P]}"|cut -d'|' -f3)
  710.                 EXTRA=$(echo "${RRDgGRAPH[$P]}"|cut -d'|' -f5)
  711.                 [ ! -z "$FORCEGRAPH" ] && {
  712.                     RET=1
  713.                 } || {
  714.                     COND=$(echo "${RRDgGRAPH[$P]}"|cut -d'|' -f4)
  715.                     [ -z "$COND" ] && RET=1 || {
  716.                         COND="if ${COND}; then RET=1; else RET=0; fi"
  717.                         eval "$COND"
  718.                     }
  719.                 }
  720.                 [ "$RET" = 1 ] && {
  721.                     echo "Making graph (${N}:${P}) ${RRDOUTPUT}/${IMGBASE}.png .."
  722.                     CreateGraph "${RRDOUTPUT}/${IMGBASE}.png" "$BACK" "${RRDgUM[$N]}" "$TITLE" "$RRDFILE" "${RRDgDEF[$N]}" "$EXTRA" "--graph-render-mode mono"
  723.                 }
  724.             done
  725.         ;;
  726.         graph_cron)
  727.             if [[ $N =~ ^[0-9]{1,3}$ ]]; then
  728.                 # grab hour and minute
  729.                 M=$(date "+%M")
  730.                 H=$(date "+%H")
  731.                 # do graphs
  732.                 if [ $CRON_GRAPH_TIME == "s" ]; then
  733.                         CRON_SUB_GRAPH=0
  734.                 elif [ $CRON_GRAPH_TIME == "h" ]; then
  735.                         CRON_SUB_GRAPH=1
  736.                 elif [ $CRON_GRAPH_TIME == "d" ]; then
  737.                         CRON_SUB_GRAPH=2
  738.                 elif [ $CRON_GRAPH_TIME == "w" ]; then
  739.                         CRON_SUB_GRAPH=3
  740.                 elif [ $CRON_GRAPH_TIME == "m" ]; then
  741.                         CRON_SUB_GRAPH=4
  742.                 elif [ $CRON_GRAPH_TIME == "y" ]; then
  743.                         CRON_SUB_GRAPH=5
  744.                 else
  745.                     exit 1
  746.                 fi
  747.                 P=$((((($N+1)*6)-6)+$CRON_SUB_GRAPH))
  748.                 [ -z "${RRDgGRAPH[$P]}" ] && continue
  749.                 BACK=$(echo "${RRDgGRAPH[$P]}"|cut -d'|' -f1)
  750.                 IMGBASE=$(echo "${RRDgGRAPH[$P]}"|cut -d'|' -f2)
  751.                 TITLE=$(echo "${RRDgGRAPH[$P]}"|cut -d'|' -f3)
  752.                 EXTRA=$(echo "${RRDgGRAPH[$P]}"|cut -d'|' -f5)
  753.                 [ ! -z "$FORCEGRAPH" ] && {
  754.                     RET=1
  755.                 } || {
  756.                     COND=$(echo "${RRDgGRAPH[$P]}"|cut -d'|' -f4)
  757.                     [ -z "$COND" ] && RET=1 || {
  758.                         COND="if ${COND}; then RET=1; else RET=0; fi"
  759.                         eval "$COND"
  760.                     }
  761.                 }
  762.                 [ "$RET" = 1 ] && {
  763.                     echo "Making graph (${N}:${P}) ${RRDOUTPUT}/${IMGBASE}.png .."
  764.                     CreateGraph "${RRDOUTPUT}/${IMGBASE}.png" "$BACK" "${RRDgUM[$N]}" "$TITLE" "$RRDFILE" "${RRDgDEF[$N]}" "$EXTRA"
  765.                 }
  766.             fi
  767.         ;;
  768.         *)
  769.             echo "Usage: rrdstorm {create|update|graph|graph_cron[s h d w m y]} 0 1 2 .."
  770.             exit 1
  771.         ;;
  772.     esac
  773. done
  774.  
  775. # close the main HTML index
  776. [ ! -z "$MAKEINDEX" ] && {
  777.     echo "</ul><p>RRDStorm v${VERSION}/${DATE}</p></body>" >> "$HTMLINDEX"
  778. }
  779.  
  780. exit 0
  781.  
  782.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement