[S'enregister] - [S'identifier]
gheop wall

BWGraph

C
par SiB (07/11/06)
Un petit test by Iris ^^'
Cacher les numéros de lignes
  1. #include <stdio.h>
  2. #include <stdint.h>
  3. #include <stdlib.h>
  4. #include <errno.h>
  5. #include <unistd.h>
  6. #include <time.h>
  7. #include <sys/sysinfo.h>
  8. #include <sys/time.h>
  9. #include <string.h>
  10.  
  11. static char *postscript_header =
  12. "%!PS\n"
  13. "<< /PageSize [ 240 128 ] >> setpagedevice\n"
  14. "0 0 240 128 rectclip 1 setlinejoin\n"
  15. "/xc { dup stringwidth pop 2 div 120 exch sub } bind def\n"
  16. "/xr { dup stringwidth pop 237 exch sub } bind def\n"
  17. "/xrh { dup stringwidth pop 145 exch sub } bind def\n"
  18. "/fitshow { gsave dup stringwidth pop 237 exch div 1 scale show grestore } bind def\n"
  19. "/c_bgh { .96 setgray } bind def\n"
  20. "/c_889 { .53 .53 .6 setrgbcolor } bind def\n"
  21. "/c_mrk { 0 0 1 setrgbcolor } bind def\n"
  22. "/c_kin { .5 .8 .5 setrgbcolor } bind def\n"
  23. "/c_kout { .8 .5 .5 setrgbcolor } bind def\n"
  24. "/Helvetica-Bold findfont 130 scalefont setfont\n"
  25. "c_bgh (%s) dup 3 28 moveto fitshow\n" /* hostname */
  26. "/Helvetica findfont 12 scalefont setfont\n"
  27. "c_889 3 3 moveto show\n"
  28. "(%s) xr 3 moveto show\n" /* uptime */
  29. "c_889 (%s) xc 20 moveto show\n" /* loadaverages */
  30. "(%02d:%02d) 3 20 moveto show\n" /* first data time */
  31. "(%02d:%02d) xr 20 moveto show\n" /* last data time */
  32. "/NewCenturySchlbk-Italic findfont 12 scalefont setfont\n"
  33. "/valshow {\n"
  34. " /ypos exch def\n"
  35. " /xpos exch def\n"
  36. " xpos 180 gt { dup stringwidth pop 1.5 add xpos exch sub }\n"
  37. " { xpos } ifelse\n"
  38. " ypos moveto\n"
  39. " gsave xpos ypos moveto\n"
  40. " ypos 64 gt {\n"
  41. " 0 5 rlineto 0 -5 rmoveto\n"
  42. " 2 2 rlineto -2 -2 rmoveto -2 2 rlineto\n"
  43. " } {\n"
  44. " 0 7 rmoveto 0 5 rlineto -2 -2 rlineto\n"
  45. " 2 2 rmoveto 2 -2 rlineto\n"
  46. " } ifelse c_mrk stroke grestore\n"
  47. " show\n"
  48. "} bind def\n"
  49. "/plotvalues {\n"
  50. " /yplot exch def\n"
  51. " /gkey exch def\n"
  52. " counttomark 1 sub /lim exch def\n"
  53. " /minidx 0 def /maxidx lim 1 sub def\n"
  54. " lim 1 add copy\n"
  55. " lim -1 0 {\n"
  56. " /idx exch def\n"
  57. " /val exch def\n"
  58. " idx lim eq { /now val def /max 0 def /min 99999999 def } if\n"
  59. " val max gt { /max val def /maxidx idx def } if\n"
  60. " val min le { /min val def /minidx idx def } if\n"
  61. " } for\n"
  62. " max (12345678) cvs maxidx yplot 64 add valshow\n"
  63. " 3 0 rmoveto gkey show\n"
  64. " min (12345678) cvs minidx yplot 12 sub valshow\n"
  65. " 3 0 rmoveto gkey show\n"
  66. " /yscale max min sub 64 div def\n"
  67. " yscale 0 eq { /yscale 12345678 def } if\n"
  68. " lim -1 0 {\n"
  69. " /idx exch def\n"
  70. " min sub yscale div yplot add /val exch def\n"
  71. " idx lim eq { idx val moveto } { idx val lineto } ifelse\n"
  72. " } for stroke\n"
  73. "} bind def\n"
  74. "newpath 238 35 moveto -3 -6 rlineto 6 0 rlineto closepath fill\n";
  75. /* mark data . . . data c_kin (kbps in) 43 plotvalues */
  76. /* mark data . . . data c_kout (kbps out) 54 plotvalues */
  77. /* showpage */
  78.  
  79. /* note that file is converted to pnm 2x the desired size so pnmscale can antialias */
  80. static char *convert_ps2pnm = "gs -q -r144 -sOutputFile=/tmp/bw.pnm -sDEVICE=pnm -dBATCH -dNOPAUSE /tmp/bw.ps";
  81. static char *convert_pnm2png = "pnmscale 0.5 -quiet /tmp/bw.pnm | pnmtopng -quiet >/tmp/bw.png";
  82. static char *convert_ps2pdf = "ps2pdf14 /tmp/bw.ps /tmp/bw.pdf";
  83. static char *movecmd = "cp -f /tmp/bw.png /www/bw-%s.png ; cp -f /tmp/bw.pdf /www/bw-%s.pdf";
  84. static char move_into_place[256];
  85.  
  86. static char myhostname[64];
  87. static char myuptime[64];
  88. static char myloadavg[64];
  89. static time_t sample_time[256], last_psfile = 0;
  90. static uint64_t kbps_in[256], kbps_out[256];
  91. static unsigned int first_new = 0, first_old = 0;
  92.  
  93. static void write_postscript(FILE *f)
  94. {
  95. struct tm old, new;
  96. int i;
  97.  
  98. gmtime_r(&sample_time[first_old & 0xff], &old);
  99. gmtime_r(&sample_time[first_new & 0xff], &new);
  100. fprintf(f, postscript_header,
  101. myhostname, myuptime, myloadavg,
  102. old.tm_hour, old.tm_min,
  103. new.tm_hour, new.tm_min);
  104. fprintf(f, "mark\n");
  105. for (i = first_old; i < first_new; i++) {
  106. fprintf(f, "%qu\n", kbps_in[i & 0xff]);
  107. }
  108. fprintf(f, "c_kin (kbps in) 43 plotvalues\nmark\n");
  109. for (i = first_old; i < first_new; i++) {
  110. fprintf(f, "%qu\n", kbps_out[i & 0xff]);
  111. }
  112. fprintf(f, "c_kout (kbps out) 54 plotvalues\nshowpage\n");
  113. }
  114.  
  115. /* this is used to read the last 32KB of hpa's bandwidth log */
  116. /* this will give the graph initial history to show on startup */
  117. static void read_bwlog(void)
  118. {
  119. FILE *f;
  120. double log_time;
  121. uint64_t log_out, log_in;
  122.  
  123. if ((f = fopen("/www/bw", "r")) == NULL) {
  124. perror("fopen(/www/bw)");
  125. return;
  126. }
  127. /* read the last 64KB of the bwlog for data */
  128. if (fseek(f, -65536, SEEK_END) < 0) {
  129. perror("fseek(/www/bw)");
  130. return;
  131. }
  132. /* synchronize on the first newline */
  133. fscanf(f, "%*[^\n] ");
  134. while (!feof(f)) {
  135. fscanf(f, "%lf %qu %qu\n", &log_time, &log_out, &log_in);
  136. if ((time_t)log_time <= sample_time[first_new & 0xff])
  137. continue;
  138. sample_time[first_new & 0xff] = (time_t)log_time;
  139. kbps_out[first_new & 0xff] = log_out >> 10;
  140. kbps_in[first_new & 0xff] = log_in >> 10;
  141. first_new++;
  142. if (first_new - first_old >= 240) {
  143. first_old++;
  144. }
  145. }
  146. fclose(f);
  147. }
  148.  
  149. int main(int argc, char *argv[])
  150. {
  151. FILE *f;
  152. char ifname[8], ethdev[8];
  153. struct timeval tv_now, tv_then;
  154. uint64_t in_now, in_then = ~0, out_now, out_then = ~0;
  155. uint64_t bw_out, bw_in;
  156. int delta_sec = 4, i;
  157.  
  158. if (argc > 1) {
  159. strncpy(ethdev, argv[1], 7);
  160. } else {
  161. strcpy(ethdev, "eth0");
  162. }
  163. if (argc > 2) {
  164. delta_sec = atoi(argv[2]);
  165. }
  166. if (delta_sec <= 0 || ethdev[0] == '-' || ethdev[0] == '?') {
  167. fprintf(stderr, "usage: %s [ethdev [interval]]\n", argv[0]);
  168. exit(1);
  169. }
  170. if (gethostname(myhostname, 63) < 0) {
  171. perror("gethostname");
  172. exit(1);
  173. }
  174. sprintf(move_into_place, movecmd, myhostname, myhostname);
  175. read_bwlog(); /* prime bandwidth history values from hpa's log */
  176. while (1) {
  177. if ((f = fopen("/proc/net/dev", "r")) == NULL) {
  178. fprintf(stderr, "%s: /proc/net/dev: %s\n",
  179. argv[0], strerror(errno));
  180. exit(1);
  181. }
  182. fscanf(f, "%*[^\n] %*[^\n] ");
  183.  
  184. i = 0;
  185. do {
  186. fscanf(f, "%7[^:]:%llu %*u %*u %*u %*u %*u %*u %*u"
  187. "%llu %*[^\n] ", &ifname, &in_now, &out_now);
  188. if (strncmp(ifname, ethdev, 7) == 0) {
  189. i++;
  190. break;
  191. }
  192. } while (!feof(f));
  193. fclose(f);
  194. if (!i) {
  195. fprintf(stderr, "device %s not found\n", ethdev);
  196. exit(1);
  197. }
  198.  
  199. gettimeofday(&tv_now, NULL);
  200. /* record time of above sample */
  201. sample_time[first_new & 0xff] = tv_now.tv_sec;
  202. if (out_then != ~0) {
  203. double sec = (double)tv_now.tv_sec +
  204. (double)tv_now.tv_usec / 1000000.0 -
  205. (double)tv_then.tv_sec -
  206. (double)tv_then.tv_usec / 1000000.0;
  207.  
  208. if (sec <= 0) {
  209. fprintf(stderr, "Time warp detected\n");
  210. exit(1);
  211. }
  212. /* handle 32-bit wrap of counters if necessary */
  213. if (in_now < in_then) {
  214. bw_in = (uint64_t)
  215. ((double)(in_now + (1ULL << 32)
  216. - in_then) / sec);
  217. } else {
  218. bw_in = (uint64_t)
  219. ((double)(in_now - in_then) / sec);
  220. }
  221. if (out_now < out_then) {
  222. bw_out = (uint64_t)
  223. ((double)(out_now + (1ULL << 32)
  224. - out_then) / sec);
  225. } else {
  226. bw_out = (uint64_t)
  227. ((double)(out_now - out_then) / sec);
  228. }
  229. bw_in >>= 7;
  230. bw_out >>= 7;
  231. kbps_in[first_new & 0xff] = bw_in;
  232. kbps_out[first_new & 0xff] = bw_out;
  233. /* flag errors in math on the generated graphs */
  234. if (kbps_in[i & 0xff] > 123456789)
  235. kbps_in[i & 0xff] = 123456789;
  236. if (kbps_out[i & 0xff] > 123456789)
  237. kbps_out[i & 0xff] = 123456789;
  238. }
  239. if (sample_time[first_new & 0xff] - last_psfile >= 20) {
  240. struct sysinfo si;
  241. last_psfile = sample_time[first_new & 0xff];
  242. /* create new postscript file every 20 seconds */
  243. /* get load averages */
  244. if (sysinfo(&si) < 0) {
  245. perror("sysinfo");
  246. bzero(&si, sizeof(si));
  247. }
  248. sprintf(myloadavg, "%.2f %.2f %.2f",
  249. si.loads[0] / 65536.0,
  250. si.loads[1] / 65536.0,
  251. si.loads[2] / 65536.0);
  252. sprintf(myuptime, "up %ldd %ldh %ldm",
  253. si.uptime / 86400,
  254. (si.uptime % 86400) / 3600,
  255. (si.uptime % 3600) / 60);
  256. if ((f = fopen("/tmp/bw.ps", "w")) == NULL) {
  257. fprintf(stderr, "%s: /tmp/bw.ps: %s",
  258. argv[0], strerror(errno));
  259. exit(1);
  260. }
  261. write_postscript(f);
  262. fclose(f);
  263. /* convert postscript file to png */
  264. if (system(convert_ps2pnm) < 0) {
  265. perror("ps2pnm");
  266. }
  267. if (system(convert_pnm2png) < 0) {
  268. perror("pnm2png");
  269. }
  270. /* convert postscript file to pdf */
  271. if (system(convert_ps2pdf) < 0) {
  272. perror("ps2pdf");
  273. }
  274. /* copy the created files in place */
  275. if (system(move_into_place) < 0) {
  276. perror("pnm2png");
  277. }
  278. }
  279.  
  280. in_then = in_now;
  281. out_then = out_now;
  282. tv_then = tv_now;
  283.  
  284. first_new++;
  285. if (first_new - first_old >= 240) {
  286. first_old++;
  287. }
  288. if ((first_new & 0xff) > (first_old & 0xff)) {
  289. first_new &= 0xff;
  290. first_old &= 0xff;
  291. }
  292. sleep(delta_sec);
  293. }
  294. }
  295.