• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/lighttpd-1.4.39/src/
1#include "server.h"
2#include "connections.h"
3#include "response.h"
4#include "connections.h"
5#include "log.h"
6
7#include "plugin.h"
8#include <sys/types.h>
9
10#include <assert.h>
11#include <fcntl.h>
12#include <stdlib.h>
13#include <stdio.h>
14#include <string.h>
15#include <unistd.h>
16#include <errno.h>
17#include <time.h>
18
19#ifdef HAVE_FORK
20/* no need for waitpid if we don't have fork */
21#include <sys/wait.h>
22#endif
23typedef struct {
24	buffer *path_rrdtool_bin;
25	buffer *path_rrd;
26
27	double requests, *requests_ptr;
28	double bytes_written, *bytes_written_ptr;
29	double bytes_read, *bytes_read_ptr;
30} plugin_config;
31
32typedef struct {
33	PLUGIN_DATA;
34
35	buffer *cmd;
36	buffer *resp;
37
38	int read_fd, write_fd;
39	pid_t rrdtool_pid;
40
41	int rrdtool_running;
42
43	plugin_config **config_storage;
44	plugin_config conf;
45} plugin_data;
46
47INIT_FUNC(mod_rrd_init) {
48	plugin_data *p;
49
50	p = calloc(1, sizeof(*p));
51
52	p->resp = buffer_init();
53	p->cmd = buffer_init();
54
55	return p;
56}
57
58FREE_FUNC(mod_rrd_free) {
59	plugin_data *p = p_d;
60	size_t i;
61
62	if (!p) return HANDLER_GO_ON;
63
64	if (p->config_storage) {
65		for (i = 0; i < srv->config_context->used; i++) {
66			plugin_config *s = p->config_storage[i];
67
68			if (NULL == s) continue;
69
70			buffer_free(s->path_rrdtool_bin);
71			buffer_free(s->path_rrd);
72
73			free(s);
74		}
75	}
76	buffer_free(p->cmd);
77	buffer_free(p->resp);
78
79	free(p->config_storage);
80
81	if (p->rrdtool_pid) {
82		int status;
83		close(p->read_fd);
84		close(p->write_fd);
85#ifdef HAVE_FORK
86		/* collect status */
87		waitpid(p->rrdtool_pid, &status, 0);
88#endif
89	}
90
91	free(p);
92
93	return HANDLER_GO_ON;
94}
95
96static int mod_rrd_create_pipe(server *srv, plugin_data *p) {
97#ifdef HAVE_FORK
98	pid_t pid;
99
100	int to_rrdtool_fds[2];
101	int from_rrdtool_fds[2];
102	if (pipe(to_rrdtool_fds)) {
103		log_error_write(srv, __FILE__, __LINE__, "ss",
104				"pipe failed: ", strerror(errno));
105		return -1;
106	}
107
108	if (pipe(from_rrdtool_fds)) {
109		log_error_write(srv, __FILE__, __LINE__, "ss",
110				"pipe failed: ", strerror(errno));
111		return -1;
112	}
113
114	/* fork, execve */
115	switch (pid = fork()) {
116	case 0: {
117		/* child */
118		char **args;
119		int argc;
120		int i = 0;
121		char *dash = "-";
122
123		/* move stdout to from_rrdtool_fd[1] */
124		close(STDOUT_FILENO);
125		dup2(from_rrdtool_fds[1], STDOUT_FILENO);
126		close(from_rrdtool_fds[1]);
127		/* not needed */
128		close(from_rrdtool_fds[0]);
129
130		/* move the stdin to to_rrdtool_fd[0] */
131		close(STDIN_FILENO);
132		dup2(to_rrdtool_fds[0], STDIN_FILENO);
133		close(to_rrdtool_fds[0]);
134		/* not needed */
135		close(to_rrdtool_fds[1]);
136
137		/* set up args */
138		argc = 3;
139		args = malloc(sizeof(*args) * argc);
140		i = 0;
141
142		args[i++] = p->conf.path_rrdtool_bin->ptr;
143		args[i++] = dash;
144		args[i  ] = NULL;
145
146		/* we don't need the client socket */
147		for (i = 3; i < 256; i++) {
148			close(i);
149		}
150
151		/* exec the cgi */
152		execv(args[0], args);
153
154		/* log_error_write(srv, __FILE__, __LINE__, "sss", "spawing rrdtool failed: ", strerror(errno), args[0]); */
155
156		/* */
157		SEGFAULT();
158		break;
159	}
160	case -1:
161		/* error */
162		log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed: ", strerror(errno));
163		break;
164	default: {
165		/* father */
166
167		close(from_rrdtool_fds[1]);
168		close(to_rrdtool_fds[0]);
169
170		/* register PID and wait for them asyncronously */
171		p->write_fd = to_rrdtool_fds[1];
172		p->read_fd = from_rrdtool_fds[0];
173		p->rrdtool_pid = pid;
174
175		fd_close_on_exec(p->write_fd);
176		fd_close_on_exec(p->read_fd);
177
178		break;
179	}
180	}
181
182	return 0;
183#else
184	return -1;
185#endif
186}
187
188/* read/write wrappers to catch EINTR */
189
190/* write to blocking socket; blocks until all data is sent, write returns 0 or an error (apart from EINTR) occurs. */
191static ssize_t safe_write(int fd, const void *buf, size_t count) {
192	ssize_t res, sum = 0;
193
194	for (;;) {
195		res = write(fd, buf, count);
196		if (res >= 0) {
197			sum += res;
198			/* do not try again if res == 0 */
199			if (res == 0 || (size_t) res == count) return sum;
200			count -= res;
201			buf = (const char*) buf + res;
202			continue;
203		}
204		switch (errno) {
205		case EINTR:
206			continue;
207		default:
208			return -1;
209		}
210	}
211}
212
213/* this assumes we get enough data on a successful read */
214static ssize_t safe_read(int fd, void *buf, size_t count) {
215	ssize_t res;
216
217	for (;;) {
218		res = read(fd, buf, count);
219		if (res >= 0) return res;
220		switch (errno) {
221		case EINTR:
222			continue;
223		default:
224			return -1;
225		}
226	}
227}
228
229static int mod_rrdtool_create_rrd(server *srv, plugin_data *p, plugin_config *s) {
230	struct stat st;
231	int r;
232
233	/* check if DB already exists */
234	if (0 == stat(s->path_rrd->ptr, &st)) {
235		/* check if it is plain file */
236		if (!S_ISREG(st.st_mode)) {
237			log_error_write(srv, __FILE__, __LINE__, "sb",
238					"not a regular file:", s->path_rrd);
239			return HANDLER_ERROR;
240		}
241
242		/* still create DB if it's empty file */
243		if (st.st_size > 0) {
244			return HANDLER_GO_ON;
245		}
246	}
247
248	/* create a new one */
249	buffer_copy_string_len(p->cmd, CONST_STR_LEN("create "));
250	buffer_append_string_buffer(p->cmd, s->path_rrd);
251	buffer_append_string_len(p->cmd, CONST_STR_LEN(
252		" --step 60 "
253		"DS:InOctets:ABSOLUTE:600:U:U "
254		"DS:OutOctets:ABSOLUTE:600:U:U "
255		"DS:Requests:ABSOLUTE:600:U:U "
256		"RRA:AVERAGE:0.5:1:600 "
257		"RRA:AVERAGE:0.5:6:700 "
258		"RRA:AVERAGE:0.5:24:775 "
259		"RRA:AVERAGE:0.5:288:797 "
260		"RRA:MAX:0.5:1:600 "
261		"RRA:MAX:0.5:6:700 "
262		"RRA:MAX:0.5:24:775 "
263		"RRA:MAX:0.5:288:797 "
264		"RRA:MIN:0.5:1:600 "
265		"RRA:MIN:0.5:6:700 "
266		"RRA:MIN:0.5:24:775 "
267		"RRA:MIN:0.5:288:797\n"));
268
269	if (-1 == (safe_write(p->write_fd, CONST_BUF_LEN(p->cmd)))) {
270		log_error_write(srv, __FILE__, __LINE__, "ss",
271			"rrdtool-write: failed", strerror(errno));
272
273		return HANDLER_ERROR;
274	}
275
276	buffer_string_prepare_copy(p->resp, 4095);
277	if (-1 == (r = safe_read(p->read_fd, p->resp->ptr, p->resp->size - 1))) {
278		log_error_write(srv, __FILE__, __LINE__, "ss",
279			"rrdtool-read: failed", strerror(errno));
280
281		return HANDLER_ERROR;
282	}
283
284	buffer_commit(p->resp, r);
285
286	if (p->resp->ptr[0] != 'O' ||
287		p->resp->ptr[1] != 'K') {
288		log_error_write(srv, __FILE__, __LINE__, "sbb",
289			"rrdtool-response:", p->cmd, p->resp);
290
291		return HANDLER_ERROR;
292	}
293
294	return HANDLER_GO_ON;
295}
296
297#define PATCH(x) \
298	p->conf.x = s->x;
299static int mod_rrd_patch_connection(server *srv, connection *con, plugin_data *p) {
300	size_t i, j;
301	plugin_config *s = p->config_storage[0];
302
303	PATCH(path_rrdtool_bin);
304	PATCH(path_rrd);
305
306	p->conf.bytes_written_ptr = &(s->bytes_written);
307	p->conf.bytes_read_ptr = &(s->bytes_read);
308	p->conf.requests_ptr = &(s->requests);
309
310	/* skip the first, the global context */
311	for (i = 1; i < srv->config_context->used; i++) {
312		data_config *dc = (data_config *)srv->config_context->data[i];
313		s = p->config_storage[i];
314
315		/* condition didn't match */
316		if (!config_check_cond(srv, con, dc)) continue;
317
318		/* merge config */
319		for (j = 0; j < dc->value->used; j++) {
320			data_unset *du = dc->value->data[j];
321
322			if (buffer_is_equal_string(du->key, CONST_STR_LEN("rrdtool.db-name"))) {
323				PATCH(path_rrd);
324				/* get pointers to double values */
325
326				p->conf.bytes_written_ptr = &(s->bytes_written);
327				p->conf.bytes_read_ptr = &(s->bytes_read);
328				p->conf.requests_ptr = &(s->requests);
329			}
330		}
331	}
332
333	return 0;
334}
335#undef PATCH
336
337SETDEFAULTS_FUNC(mod_rrd_set_defaults) {
338	plugin_data *p = p_d;
339	size_t i;
340
341	config_values_t cv[] = {
342		{ "rrdtool.binary",  NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
343		{ "rrdtool.db-name", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
344		{ NULL,              NULL, T_CONFIG_UNSET,  T_CONFIG_SCOPE_UNSET      }
345	};
346
347	if (!p) return HANDLER_ERROR;
348
349	force_assert(srv->config_context->used > 0);
350	p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
351
352	for (i = 0; i < srv->config_context->used; i++) {
353		data_config const* config = (data_config const*)srv->config_context->data[i];
354		plugin_config *s;
355
356		s = calloc(1, sizeof(plugin_config));
357		s->path_rrdtool_bin = buffer_init();
358		s->path_rrd = buffer_init();
359		s->requests = 0;
360		s->bytes_written = 0;
361		s->bytes_read = 0;
362
363		cv[0].destination = s->path_rrdtool_bin;
364		cv[1].destination = s->path_rrd;
365
366		p->config_storage[i] = s;
367
368		if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
369			return HANDLER_ERROR;
370		}
371
372		if (i > 0 && !buffer_string_is_empty(s->path_rrdtool_bin)) {
373			/* path_rrdtool_bin is a global option */
374
375			log_error_write(srv, __FILE__, __LINE__, "s",
376					"rrdtool.binary can only be set as a global option.");
377
378			return HANDLER_ERROR;
379		}
380
381	}
382
383	p->conf.path_rrdtool_bin = p->config_storage[0]->path_rrdtool_bin;
384	p->rrdtool_running = 0;
385
386	/* check for dir */
387
388	if (buffer_string_is_empty(p->conf.path_rrdtool_bin)) {
389		log_error_write(srv, __FILE__, __LINE__, "s",
390				"rrdtool.binary has to be set");
391		return HANDLER_ERROR;
392	}
393
394	/* open the pipe to rrdtool */
395	if (mod_rrd_create_pipe(srv, p)) {
396		return HANDLER_ERROR;
397	}
398
399	p->rrdtool_running = 1;
400
401	return HANDLER_GO_ON;
402}
403
404TRIGGER_FUNC(mod_rrd_trigger) {
405	plugin_data *p = p_d;
406	size_t i;
407
408	if (!p->rrdtool_running) return HANDLER_GO_ON;
409	if ((srv->cur_ts % 60) != 0) return HANDLER_GO_ON;
410
411	for (i = 0; i < srv->config_context->used; i++) {
412		plugin_config *s = p->config_storage[i];
413		int r;
414
415		if (buffer_string_is_empty(s->path_rrd)) continue;
416
417		/* write the data down every minute */
418
419		if (HANDLER_GO_ON != mod_rrdtool_create_rrd(srv, p, s)) return HANDLER_ERROR;
420
421		buffer_copy_string_len(p->cmd, CONST_STR_LEN("update "));
422		buffer_append_string_buffer(p->cmd, s->path_rrd);
423		buffer_append_string_len(p->cmd, CONST_STR_LEN(" N:"));
424		buffer_append_int(p->cmd, s->bytes_read);
425		buffer_append_string_len(p->cmd, CONST_STR_LEN(":"));
426		buffer_append_int(p->cmd, s->bytes_written);
427		buffer_append_string_len(p->cmd, CONST_STR_LEN(":"));
428		buffer_append_int(p->cmd, s->requests);
429		buffer_append_string_len(p->cmd, CONST_STR_LEN("\n"));
430
431		if (-1 == (r = safe_write(p->write_fd, CONST_BUF_LEN(p->cmd)))) {
432			p->rrdtool_running = 0;
433
434			log_error_write(srv, __FILE__, __LINE__, "ss",
435					"rrdtool-write: failed", strerror(errno));
436
437			return HANDLER_ERROR;
438		}
439
440		buffer_string_prepare_copy(p->resp, 4095);
441		if (-1 == (r = safe_read(p->read_fd, p->resp->ptr, p->resp->size - 1))) {
442			p->rrdtool_running = 0;
443
444			log_error_write(srv, __FILE__, __LINE__, "ss",
445					"rrdtool-read: failed", strerror(errno));
446
447			return HANDLER_ERROR;
448		}
449
450		buffer_commit(p->resp, r);
451
452		if (p->resp->ptr[0] != 'O' ||
453		    p->resp->ptr[1] != 'K') {
454			/* don't fail on this error if we just started (graceful restart, the old one might have just updated too) */
455			if (!(strstr(p->resp->ptr, "(minimum one second step)") && (srv->cur_ts - srv->startup_ts < 3))) {
456				p->rrdtool_running = 0;
457
458				log_error_write(srv, __FILE__, __LINE__, "sbb",
459					"rrdtool-response:", p->cmd, p->resp);
460
461				return HANDLER_ERROR;
462			}
463		}
464		s->requests = 0;
465		s->bytes_written = 0;
466		s->bytes_read = 0;
467	}
468
469	return HANDLER_GO_ON;
470}
471
472REQUESTDONE_FUNC(mod_rrd_account) {
473	plugin_data *p = p_d;
474
475	mod_rrd_patch_connection(srv, con, p);
476
477	*(p->conf.requests_ptr)      += 1;
478	*(p->conf.bytes_written_ptr) += con->bytes_written;
479	*(p->conf.bytes_read_ptr)    += con->bytes_read;
480
481	return HANDLER_GO_ON;
482}
483
484int mod_rrdtool_plugin_init(plugin *p);
485int mod_rrdtool_plugin_init(plugin *p) {
486	p->version     = LIGHTTPD_VERSION_ID;
487	p->name        = buffer_init_string("rrd");
488
489	p->init        = mod_rrd_init;
490	p->cleanup     = mod_rrd_free;
491	p->set_defaults= mod_rrd_set_defaults;
492
493	p->handle_trigger      = mod_rrd_trigger;
494	p->handle_request_done = mod_rrd_account;
495
496	p->data        = NULL;
497
498	return 0;
499}
500