4 #include "connections.h"
6 #include "connections.h"
10 #include <sys/types.h>
22 /* no need for waitpid if we don't have fork */
26 buffer
*path_rrdtool_bin
;
29 double requests
, *requests_ptr
;
30 double bytes_written
, *bytes_written_ptr
;
31 double bytes_read
, *bytes_read_ptr
;
40 int read_fd
, write_fd
;
45 plugin_config
**config_storage
;
49 INIT_FUNC(mod_rrd_init
) {
52 p
= calloc(1, sizeof(*p
));
54 p
->resp
= buffer_init();
55 p
->cmd
= buffer_init();
60 FREE_FUNC(mod_rrd_free
) {
64 if (!p
) return HANDLER_GO_ON
;
66 if (p
->config_storage
) {
67 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
68 plugin_config
*s
= p
->config_storage
[i
];
70 if (NULL
== s
) continue;
72 buffer_free(s
->path_rrdtool_bin
);
73 buffer_free(s
->path_rrd
);
81 free(p
->config_storage
);
89 waitpid(p
->rrdtool_pid
, &status
, 0);
98 static int mod_rrd_create_pipe(server
*srv
, plugin_data
*p
) {
102 int to_rrdtool_fds
[2];
103 int from_rrdtool_fds
[2];
104 if (pipe(to_rrdtool_fds
)) {
105 log_error_write(srv
, __FILE__
, __LINE__
, "ss",
106 "pipe failed: ", strerror(errno
));
110 if (pipe(from_rrdtool_fds
)) {
111 log_error_write(srv
, __FILE__
, __LINE__
, "ss",
112 "pipe failed: ", strerror(errno
));
117 switch (pid
= fork()) {
125 /* move stdout to from_rrdtool_fd[1] */
126 close(STDOUT_FILENO
);
127 dup2(from_rrdtool_fds
[1], STDOUT_FILENO
);
128 close(from_rrdtool_fds
[1]);
130 close(from_rrdtool_fds
[0]);
132 /* move the stdin to to_rrdtool_fd[0] */
134 dup2(to_rrdtool_fds
[0], STDIN_FILENO
);
135 close(to_rrdtool_fds
[0]);
137 close(to_rrdtool_fds
[1]);
141 args
= malloc(sizeof(*args
) * argc
);
144 args
[i
++] = p
->conf
.path_rrdtool_bin
->ptr
;
148 /* we don't need the client socket */
149 for (i
= 3; i
< 256; i
++) {
154 execv(args
[0], args
);
156 /* log_error_write(srv, __FILE__, __LINE__, "sss", "spawing rrdtool failed: ", strerror(errno), args[0]); */
164 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "fork failed: ", strerror(errno
));
169 close(from_rrdtool_fds
[1]);
170 close(to_rrdtool_fds
[0]);
172 /* register PID and wait for them asyncronously */
173 p
->write_fd
= to_rrdtool_fds
[1];
174 p
->read_fd
= from_rrdtool_fds
[0];
175 p
->rrdtool_pid
= pid
;
177 fd_close_on_exec(p
->write_fd
);
178 fd_close_on_exec(p
->read_fd
);
190 /* read/write wrappers to catch EINTR */
192 /* write to blocking socket; blocks until all data is sent, write returns 0 or an error (apart from EINTR) occurs. */
193 static ssize_t
safe_write(int fd
, const void *buf
, size_t count
) {
194 ssize_t res
, sum
= 0;
197 res
= write(fd
, buf
, count
);
200 /* do not try again if res == 0 */
201 if (res
== 0 || (size_t) res
== count
) return sum
;
203 buf
= (const char*) buf
+ res
;
215 /* this assumes we get enough data on a successful read */
216 static ssize_t
safe_read(int fd
, void *buf
, size_t count
) {
220 res
= read(fd
, buf
, count
);
221 if (res
>= 0) return res
;
231 static int mod_rrdtool_create_rrd(server
*srv
, plugin_data
*p
, plugin_config
*s
) {
235 /* check if DB already exists */
236 if (0 == stat(s
->path_rrd
->ptr
, &st
)) {
237 /* check if it is plain file */
238 if (!S_ISREG(st
.st_mode
)) {
239 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
240 "not a regular file:", s
->path_rrd
);
241 return HANDLER_ERROR
;
244 /* still create DB if it's empty file */
245 if (st
.st_size
> 0) {
246 return HANDLER_GO_ON
;
250 /* create a new one */
251 buffer_copy_string_len(p
->cmd
, CONST_STR_LEN("create "));
252 buffer_append_string_buffer(p
->cmd
, s
->path_rrd
);
253 buffer_append_string_len(p
->cmd
, CONST_STR_LEN(
255 "DS:InOctets:ABSOLUTE:600:U:U "
256 "DS:OutOctets:ABSOLUTE:600:U:U "
257 "DS:Requests:ABSOLUTE:600:U:U "
258 "RRA:AVERAGE:0.5:1:600 "
259 "RRA:AVERAGE:0.5:6:700 "
260 "RRA:AVERAGE:0.5:24:775 "
261 "RRA:AVERAGE:0.5:288:797 "
264 "RRA:MAX:0.5:24:775 "
265 "RRA:MAX:0.5:288:797 "
268 "RRA:MIN:0.5:24:775 "
269 "RRA:MIN:0.5:288:797\n"));
271 if (-1 == (safe_write(p
->write_fd
, CONST_BUF_LEN(p
->cmd
)))) {
272 log_error_write(srv
, __FILE__
, __LINE__
, "ss",
273 "rrdtool-write: failed", strerror(errno
));
275 return HANDLER_ERROR
;
278 buffer_string_prepare_copy(p
->resp
, 4095);
279 if (-1 == (r
= safe_read(p
->read_fd
, p
->resp
->ptr
, p
->resp
->size
- 1))) {
280 log_error_write(srv
, __FILE__
, __LINE__
, "ss",
281 "rrdtool-read: failed", strerror(errno
));
283 return HANDLER_ERROR
;
286 buffer_commit(p
->resp
, r
);
288 if (p
->resp
->ptr
[0] != 'O' ||
289 p
->resp
->ptr
[1] != 'K') {
290 log_error_write(srv
, __FILE__
, __LINE__
, "sbb",
291 "rrdtool-response:", p
->cmd
, p
->resp
);
293 return HANDLER_ERROR
;
296 return HANDLER_GO_ON
;
301 static int mod_rrd_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
303 plugin_config
*s
= p
->config_storage
[0];
305 PATCH(path_rrdtool_bin
);
308 p
->conf
.bytes_written_ptr
= &(s
->bytes_written
);
309 p
->conf
.bytes_read_ptr
= &(s
->bytes_read
);
310 p
->conf
.requests_ptr
= &(s
->requests
);
312 /* skip the first, the global context */
313 for (i
= 1; i
< srv
->config_context
->used
; i
++) {
314 data_config
*dc
= (data_config
*)srv
->config_context
->data
[i
];
315 s
= p
->config_storage
[i
];
317 /* condition didn't match */
318 if (!config_check_cond(srv
, con
, dc
)) continue;
321 for (j
= 0; j
< dc
->value
->used
; j
++) {
322 data_unset
*du
= dc
->value
->data
[j
];
324 if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("rrdtool.db-name"))) {
326 /* get pointers to double values */
328 p
->conf
.bytes_written_ptr
= &(s
->bytes_written
);
329 p
->conf
.bytes_read_ptr
= &(s
->bytes_read
);
330 p
->conf
.requests_ptr
= &(s
->requests
);
339 SETDEFAULTS_FUNC(mod_rrd_set_defaults
) {
340 plugin_data
*p
= p_d
;
343 config_values_t cv
[] = {
344 { "rrdtool.binary", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
}, /* 0 */
345 { "rrdtool.db-name", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
}, /* 1 */
346 { NULL
, NULL
, T_CONFIG_UNSET
, T_CONFIG_SCOPE_UNSET
}
349 if (!p
) return HANDLER_ERROR
;
351 force_assert(srv
->config_context
->used
> 0);
352 p
->config_storage
= calloc(1, srv
->config_context
->used
* sizeof(plugin_config
*));
354 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
355 data_config
const* config
= (data_config
const*)srv
->config_context
->data
[i
];
358 s
= calloc(1, sizeof(plugin_config
));
359 s
->path_rrdtool_bin
= buffer_init();
360 s
->path_rrd
= buffer_init();
362 s
->bytes_written
= 0;
365 cv
[0].destination
= s
->path_rrdtool_bin
;
366 cv
[1].destination
= s
->path_rrd
;
368 p
->config_storage
[i
] = s
;
370 if (0 != config_insert_values_global(srv
, config
->value
, cv
, i
== 0 ? T_CONFIG_SCOPE_SERVER
: T_CONFIG_SCOPE_CONNECTION
)) {
371 return HANDLER_ERROR
;
374 if (i
> 0 && !buffer_string_is_empty(s
->path_rrdtool_bin
)) {
375 /* path_rrdtool_bin is a global option */
377 log_error_write(srv
, __FILE__
, __LINE__
, "s",
378 "rrdtool.binary can only be set as a global option.");
380 return HANDLER_ERROR
;
385 p
->conf
.path_rrdtool_bin
= p
->config_storage
[0]->path_rrdtool_bin
;
386 p
->rrdtool_running
= 0;
390 if (buffer_string_is_empty(p
->conf
.path_rrdtool_bin
)) {
391 log_error_write(srv
, __FILE__
, __LINE__
, "s",
392 "rrdtool.binary has to be set");
393 return HANDLER_ERROR
;
396 /* open the pipe to rrdtool */
397 if (mod_rrd_create_pipe(srv
, p
)) {
398 return HANDLER_ERROR
;
401 p
->rrdtool_running
= 1;
403 return HANDLER_GO_ON
;
406 TRIGGER_FUNC(mod_rrd_trigger
) {
407 plugin_data
*p
= p_d
;
410 if (!p
->rrdtool_running
) return HANDLER_GO_ON
;
411 if ((srv
->cur_ts
% 60) != 0) return HANDLER_GO_ON
;
413 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
414 plugin_config
*s
= p
->config_storage
[i
];
417 if (buffer_string_is_empty(s
->path_rrd
)) continue;
419 /* write the data down every minute */
421 if (HANDLER_GO_ON
!= mod_rrdtool_create_rrd(srv
, p
, s
)) return HANDLER_ERROR
;
423 buffer_copy_string_len(p
->cmd
, CONST_STR_LEN("update "));
424 buffer_append_string_buffer(p
->cmd
, s
->path_rrd
);
425 buffer_append_string_len(p
->cmd
, CONST_STR_LEN(" N:"));
426 buffer_append_int(p
->cmd
, s
->bytes_read
);
427 buffer_append_string_len(p
->cmd
, CONST_STR_LEN(":"));
428 buffer_append_int(p
->cmd
, s
->bytes_written
);
429 buffer_append_string_len(p
->cmd
, CONST_STR_LEN(":"));
430 buffer_append_int(p
->cmd
, s
->requests
);
431 buffer_append_string_len(p
->cmd
, CONST_STR_LEN("\n"));
433 if (-1 == (r
= safe_write(p
->write_fd
, CONST_BUF_LEN(p
->cmd
)))) {
434 p
->rrdtool_running
= 0;
436 log_error_write(srv
, __FILE__
, __LINE__
, "ss",
437 "rrdtool-write: failed", strerror(errno
));
439 return HANDLER_ERROR
;
442 buffer_string_prepare_copy(p
->resp
, 4095);
443 if (-1 == (r
= safe_read(p
->read_fd
, p
->resp
->ptr
, p
->resp
->size
- 1))) {
444 p
->rrdtool_running
= 0;
446 log_error_write(srv
, __FILE__
, __LINE__
, "ss",
447 "rrdtool-read: failed", strerror(errno
));
449 return HANDLER_ERROR
;
452 buffer_commit(p
->resp
, r
);
454 if (p
->resp
->ptr
[0] != 'O' ||
455 p
->resp
->ptr
[1] != 'K') {
456 /* don't fail on this error if we just started (graceful restart, the old one might have just updated too) */
457 if (!(strstr(p
->resp
->ptr
, "(minimum one second step)") && (srv
->cur_ts
- srv
->startup_ts
< 3))) {
458 p
->rrdtool_running
= 0;
460 log_error_write(srv
, __FILE__
, __LINE__
, "sbb",
461 "rrdtool-response:", p
->cmd
, p
->resp
);
463 return HANDLER_ERROR
;
467 s
->bytes_written
= 0;
471 return HANDLER_GO_ON
;
474 REQUESTDONE_FUNC(mod_rrd_account
) {
475 plugin_data
*p
= p_d
;
477 mod_rrd_patch_connection(srv
, con
, p
);
479 *(p
->conf
.requests_ptr
) += 1;
480 *(p
->conf
.bytes_written_ptr
) += con
->bytes_written
;
481 *(p
->conf
.bytes_read_ptr
) += con
->bytes_read
;
483 return HANDLER_GO_ON
;
486 int mod_rrdtool_plugin_init(plugin
*p
);
487 int mod_rrdtool_plugin_init(plugin
*p
) {
488 p
->version
= LIGHTTPD_VERSION_ID
;
489 p
->name
= buffer_init_string("rrd");
491 p
->init
= mod_rrd_init
;
492 p
->cleanup
= mod_rrd_free
;
493 p
->set_defaults
= mod_rrd_set_defaults
;
495 p
->handle_trigger
= mod_rrd_trigger
;
496 p
->handle_request_done
= mod_rrd_account
;