1 /*
2  * Copyright (c) 2002-2006, Daniel Hartmeier
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  *    - Redistributions of source code must retain the above copyright
10  *      notice, this list of conditions and the following disclaimer.
11  *    - Redistributions in binary form must reproduce the above
12  *      copyright notice, this list of conditions and the following
13  *      disclaimer in the documentation and/or other materials provided
14  *      with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  *
29  */
30 
31 #include <sys/types.h>
32 #include <sys/ioctl.h>
33 #include <sys/socket.h>
34 #include <netinet/in.h>
35 #include <net/if.h>
36 #include <err.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <time.h>
43 #include <unistd.h>
44 #include <gd.h>
45 #include <gdfonts.h>
46 
47 #include "graph.h"
48 
49 extern int	 debug;
50 
51 double	 intersect(double, double, double, double);
52 void	 find_max(struct matrix *);
53 void	 normalize(struct matrix *);
54 void	 draw(gdImagePtr, struct matrix *, struct graph *, int, unsigned);
55 void	 draw_grid(gdImagePtr, struct matrix *);
56 
57 int
graph_add_matrix(struct matrix ** matrices,const char * filename,unsigned type,unsigned width,unsigned height,unsigned beg,unsigned end)58 graph_add_matrix(struct matrix **matrices, const char *filename, unsigned type,
59     unsigned width, unsigned height, unsigned beg, unsigned end)
60 {
61 	struct matrix *m;
62 	const unsigned fw = gdFontSmall->w, fh = gdFontSmall->h;
63 
64 	m = malloc(sizeof(struct matrix));
65 	if (m == NULL)
66 		err(1, "malloc");
67 	m->filename = strdup(filename);
68 	if (m->filename == NULL)
69 		err(1, "strdup");
70 	m->beg = beg;
71 	m->end = end;
72 	m->type = type;
73 	m->width = width;
74 	m->height = height;
75 	m->w0 = width-fh-18*fw-2*fw;
76 	m->h0 = height-5*fh;
77 	m->x0 = fh+9*fw;
78 	m->y0 = fh;
79 	m->graphs[0] = m->graphs[1] = NULL;
80 	if (*matrices == NULL)
81 		m->next = NULL;
82 	else
83 		m->next = *matrices;
84 	*matrices = m;
85 	return (0);
86 }
87 
88 int
graph_add_graph(struct graph ** graphs,unsigned width,unsigned desc_nr,const char * label,const char * unit,unsigned color,int filled,int bytes,int type)89 graph_add_graph(struct graph **graphs, unsigned width, unsigned desc_nr,
90     const char *label, const char *unit, unsigned color, int filled,
91     int bytes, int type)
92 {
93 	unsigned i;
94 	struct graph *g;
95 
96 	g = malloc(sizeof(struct graph));
97 	if (g == NULL)
98 		err(1, "malloc");
99 	g->unit = strdup(unit);
100 	if (g->unit == NULL)
101 		err(1, "strdup");
102 	g->desc_nr = desc_nr;
103 	g->label = strdup(label);
104 	if (g->label == NULL)
105 		err(1, "strdup");
106 	g->color = color;
107 	g->filled = filled;
108 	g->bytes = bytes;
109 	g->type = type;
110 	g->data = malloc(width * sizeof(double));
111 	if (g->data == NULL)
112 		err(1, "graph_add_graph: malloc");
113 	for (i = 0; i < width; ++i)
114 		g->data[i] = 0.0;
115 	g->data_max = 0.0;
116 	if (*graphs == NULL)
117 		g->next = NULL;
118 	else
119 		g->next = *graphs;
120 	*graphs = g;
121 	return (0);
122 }
123 
124 int
graph_generate_images(struct matrix * matrices)125 graph_generate_images(struct matrix *matrices)
126 {
127 	struct matrix *matrix;
128 	const unsigned fw = gdFontSmall->w, fh = gdFontSmall->h;
129 
130 	matrix = matrices;
131 	while (matrix != NULL) {
132 		gdImagePtr im;
133 		int white;
134 		FILE *out;
135 		struct matrix *old_matrix;
136 		struct graph *graph, *old_graph;
137 		unsigned i, x;
138 
139 		if (debug > 0)
140 			printf("graph_generate_images: generating file %s\n",
141 			    matrix->filename);
142 
143 		find_max(matrix);
144 		for (i = 0; i < 2; ++i) {
145 			double m = 0.0;
146 
147 			for (graph = matrix->graphs[i]; graph;
148 			    graph = graph->next)
149 				if (graph->data_max > m)
150 					m = graph->data_max;
151 			for (graph = matrix->graphs[i]; graph;
152 			    graph = graph->next)
153 				graph->data_max = m;
154 			if (debug)
155 				printf("graph_generate_images: maximum %s "
156 				    "%.2f\n", i ? "right" : "left", m);
157 		}
158 		normalize(matrix);
159 
160 		im = gdImageCreate(matrix->width, matrix->height);
161 		white = gdImageColorAllocate(im, 255, 255, 255);
162 
163 		gdImageFilledRectangle(im, 0, 0, matrix->width-1,
164 		    matrix->height-1, white);
165 
166 		draw_grid(im, matrix);
167 
168 		for (i = 0; i < 2; ++i) {
169 			x = i == 0 ? matrix->x0 : matrix->x0+matrix->w0;
170 			for (graph = matrix->graphs[i]; graph;
171 			    graph = graph->next) {
172 				int color = gdImageColorAllocate(im,
173 				    (graph->color >> 16) & 0xFF,
174 				    (graph->color >> 8) & 0xFF,
175 				    graph->color & 0xFF);
176 
177 				draw(im, matrix, graph,
178 				    color, graph->filled);
179 				if (i)
180 					x -= strlen(graph->label) * fw;
181 				gdImageString(im, gdFontSmall, x,
182 				    matrix->y0+matrix->h0+5+fh,
183 				    graph->label, color);
184 				if (!i)
185 					x += (strlen(graph->label) + 1) * fw;
186 				else
187 					x -= fw;
188 			}
189 		}
190 
191 		out = fopen(matrix->filename, "wb");
192 		if (out == NULL) {
193 			warn("%s", matrix->filename);
194 			return (1);
195 		}
196 		if (matrix->type == 0)
197 			gdImageJpeg(im, out, 95);
198 		else
199 			gdImagePng(im, out);
200 		fclose(out);
201 		gdImageDestroy(im);
202 
203 		/* free matrix */
204 		graph = matrix->graphs[0];
205 		while (graph != NULL) {
206 			old_graph = graph;
207 			graph = graph->next;
208 			free(old_graph);
209 		}
210 		graph = matrix->graphs[1];
211 		while (graph != NULL) {
212 			old_graph = graph;
213 			graph = graph->next;
214 			free(old_graph);
215 		}
216 		free(matrix->filename);
217 		old_matrix = matrix;
218 		matrix = matrix->next;
219 		free(old_matrix);
220 	}
221 
222 	return (0);
223 }
224 
225 void
find_max(struct matrix * m)226 find_max(struct matrix *m)
227 {
228 	unsigned i, j;
229 	struct graph *g;
230 
231 	for (i = 0; i < 2; ++i)
232 		for (g = m->graphs[i]; g != NULL; g = g->next) {
233 			g->data_max = 0.0;
234 			for (j = 0; j < m->w0; ++j)
235 				if (g->data[j] > g->data_max)
236 					g->data_max = g->data[j];
237 		}
238 }
239 
240 void
normalize(struct matrix * m)241 normalize(struct matrix *m)
242 {
243 	unsigned i, j;
244 	struct graph *g;
245 
246 	for (i = 0; i < 2; ++i)
247 		for (g = m->graphs[i]; g != NULL; g = g->next)
248 			if (g->data_max != 0.0)
249 				for (j = 0; j < m->w0; ++j)
250 					g->data[j] /= g->data_max;
251 }
252 
253 void
draw(gdImagePtr im,struct matrix * m,struct graph * g,int color,unsigned filled)254 draw(gdImagePtr im, struct matrix *m, struct graph *g, int color,
255     unsigned filled)
256 {
257 	unsigned x = m->x0, y = m->y0, w = m->w0, h = m->h0;
258 	unsigned dx, dy0 = 0;
259 
260 	for (dx = 0; dx < w; ++dx) {
261 		unsigned dy = g->data[dx] * h;
262 
263 		if (filled)
264 			gdImageLine(im, x+dx+1, y+h-1, x+dx+1, y+h-1-dy,
265 			    color);
266 		else if (dx > 0)
267 			gdImageLine(im, x+dx, y+h-1-dy0, x+dx+1, y+h-1-dy,
268 			    color);
269 		dy0 = dy;
270 	}
271 }
272 
273 void
scale_unit(double * m,char * k,int bytes)274 scale_unit(double *m, char *k, int bytes)
275 {
276 	*k = ' ';
277 	if (bytes)
278 		*m *= 8.0;
279 	if (*m >= 1000) {
280 		*m /= bytes ? 1024 : 1000;
281 		*k = 'k';
282 	}
283 	if (*m >= 1000) {
284 		*m /= bytes ? 1024 : 1000;
285 		*k = 'm';
286 	}
287 	if (*m >= 1000) {
288 		*m /= bytes ? 1024 : 1000;
289 		*k = 'g';
290 	}
291 	if (*m >= 1000) {
292 		*m /= bytes ? 1024 : 1000;
293 		*k = 't';
294 	}
295 }
296 
297 void
draw_grid(gdImagePtr im,struct matrix * matrix)298 draw_grid(gdImagePtr im, struct matrix *matrix)
299 {
300 	const unsigned fw = gdFontSmall->w, fh = gdFontSmall->h;
301 	unsigned x0 = matrix->x0, y0 = matrix->y0,
302 	    w0 = matrix->w0, h0 = matrix->h0;
303 	unsigned len = matrix->end - matrix->beg;
304 	double max[2] = { 0.0, 0.0 };
305 	const char *unit[2] = { NULL, NULL };
306 	unsigned i, ii;
307 	char k[2];
308 	double dx;
309 	char *t;
310 	int black = gdImageColorAllocate(im,   0,   0,   0);
311 	int grey = gdImageColorAllocate(im, 220, 220, 220);
312 
313 	if (matrix->graphs[0] != NULL) {
314 		max[0] = matrix->graphs[0]->data_max;
315 		scale_unit(&max[0], &k[0], matrix->graphs[0]->bytes);
316 		unit[0] = matrix->graphs[0]->unit;
317 	}
318 	if (matrix->graphs[1] != NULL) {
319 		max[1] = matrix->graphs[1]->data_max;
320 		scale_unit(&max[1], &k[1], matrix->graphs[1]->bytes);
321 		unit[1] = matrix->graphs[1]->unit;
322 	}
323 
324 	/* bounding box */
325 	gdImageLine(im, x0, y0+h0, x0+w0, y0+h0, black);
326 	gdImageLine(im, x0, y0, x0, y0+h0, black);
327 	gdImageLine(im, x0+w0, y0, x0+w0, y0+h0, black);
328 
329 	/* horizontal units */
330 	ii = h0 / fh;
331 	if (ii > 10)
332 		ii = 10;
333 	for (i = 0; i <= ii; ++i) {
334 		char t[10];
335 		unsigned y1 = y0+(i*h0)/ii;
336 
337 		gdImageLine(im, x0-2, y1, x0, y1, black);
338 		gdImageLine(im, x0+w0, y1, x0+w0+2, y1, black);
339 		if (i < ii)
340 			gdImageLine(im, x0+1, y1, x0+w0-1, y1, grey);
341 		if (matrix->graphs[0] != NULL) {
342 			snprintf(t, sizeof(t), "%5.1f %c",
343 			    (max[0]*(ii-i))/ii, k[0]);
344 			gdImageString(im, gdFontSmall, fh+fw,
345 			    y1-fh/2, t, black);
346 		}
347 		if (matrix->graphs[1] != NULL) {
348 			snprintf(t, sizeof(t), "%5.1f %c",
349 			    (max[1]*(ii-i))/ii, k[1]);
350 			gdImageString(im, gdFontSmall, x0+w0+1*fw,
351 			    y1-fh/2, t, black);
352 		}
353 	}
354 
355 	/* time label */
356 	t = "minutes";
357 	dx = (60.0 * (double)w0) / (double)len; // pixels per unit
358 	if (dx < 4*fw) {
359 		dx *= 60.0; t = "hours";
360 		if (dx < 4*fw) {
361 			dx *= 24.0; t = "days";
362 			if (dx < 4*fw) {
363 				dx *= 7.0; t = "weeks";
364 				if (dx < 4*fw) {
365 					dx *= 30.0 / 7.0; t = "months";
366 					if (dx < 4*fw) {
367 						dx *= 365.0 / 30.0;
368 						t = "years";
369 					}
370 				}
371 			}
372 		}
373 	}
374 	/* time units */
375 	for (i = 0; (i*dx) <= w0; ++i) {
376 		unsigned x1 = x0+w0-(i*dx);
377 		char t[64];
378 
379 		gdImageLine(im, x1, y0+h0, x1, y0+h0+2, black);
380 		if (x1 > x0 && x1 < x0+w0)
381 			gdImageLine(im, x1, y0+h0, x1, y0, grey);
382 		if (x1 < x0+w0) {
383 			snprintf(t, sizeof(t), "-%u", i);
384 			gdImageString(im, gdFontSmall, x1-(strlen(t)*fw)/2,
385 			    y0+h0+5, t, black);
386 		} else {
387 			time_t tt = time(0);
388 			strncpy(t, asctime(localtime(&tt)), 24); t[24] = 0;
389 			gdImageString(im, gdFontSmall, x0+(w0-strlen(t)*fw)/2,
390 			    y0+h0+5+2*fh, t, black);
391 		}
392 	}
393 	/* hours/days/weeks/months */
394 	gdImageString(im, gdFontSmall, x0+w0-strlen(t)*fw, y0+h0+5, t, black);
395 	if (matrix->graphs[0] != NULL)
396 		gdImageStringUp(im, gdFontSmall, 0,
397 		    y0+h0-(h0-strlen(unit[0])*fw)/2, (char *)unit[0], black);
398 	if (matrix->graphs[1] != NULL)
399 		gdImageStringUp(im, gdFontSmall, x0+w0+8*fw,
400 		    y0+h0-(h0-strlen(unit[1])*fw)/2, (char *)unit[1], black);
401 }
402 
403